aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig41
-rw-r--r--sound/aoa/Kconfig11
-rw-r--r--sound/aoa/codecs/Kconfig4
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-tas.c6
-rw-r--r--sound/aoa/fabrics/Kconfig1
-rw-r--r--sound/aoa/soundbus/Kconfig1
-rw-r--r--sound/aoa/soundbus/soundbus.h2
-rw-r--r--sound/arm/Kconfig30
-rw-r--r--sound/arm/Makefile4
-rw-r--r--sound/arm/aaci.c6
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c384
-rw-r--r--sound/arm/pxa2xx-ac97.c257
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c278
-rw-r--r--sound/arm/pxa2xx-pcm.c252
-rw-r--r--sound/arm/pxa2xx-pcm.h13
-rw-r--r--sound/arm/sa11xx-uda1341.c13
-rw-r--r--sound/core/Kconfig39
-rw-r--r--sound/core/Makefile1
-rw-r--r--sound/core/control.c61
-rw-r--r--sound/core/control_compat.c3
-rw-r--r--sound/core/device.c26
-rw-r--r--sound/core/hwdep.c16
-rw-r--r--sound/core/info.c24
-rw-r--r--sound/core/info_oss.c6
-rw-r--r--sound/core/init.c73
-rw-r--r--sound/core/jack.c163
-rw-r--r--sound/core/memalloc.c162
-rw-r--r--sound/core/oss/copy.c30
-rw-r--r--sound/core/oss/io.c24
-rw-r--r--sound/core/oss/linear.c29
-rw-r--r--sound/core/oss/mixer_oss.c18
-rw-r--r--sound/core/oss/mulaw.c27
-rw-r--r--sound/core/oss/pcm_oss.c52
-rw-r--r--sound/core/oss/pcm_plugin.c38
-rw-r--r--sound/core/oss/rate.c42
-rw-r--r--sound/core/oss/route.c12
-rw-r--r--sound/core/pcm.c88
-rw-r--r--sound/core/pcm_compat.c3
-rw-r--r--sound/core/pcm_lib.c102
-rw-r--r--sound/core/pcm_memory.c40
-rw-r--r--sound/core/pcm_native.c159
-rw-r--r--sound/core/pcm_timer.c6
-rw-r--r--sound/core/rawmidi.c29
-rw-r--r--sound/core/rtctimer.c6
-rw-r--r--sound/core/seq/oss/seq_oss.c12
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c9
-rw-r--r--sound/core/seq/seq_clientmgr.c32
-rw-r--r--sound/core/seq/seq_compat.c3
-rw-r--r--sound/core/seq/seq_device.c12
-rw-r--r--sound/core/seq/seq_fifo.c15
-rw-r--r--sound/core/seq/seq_memory.c12
-rw-r--r--sound/core/seq/seq_midi.c15
-rw-r--r--sound/core/seq/seq_ports.c13
-rw-r--r--sound/core/seq/seq_prioq.c4
-rw-r--r--sound/core/seq/seq_queue.c6
-rw-r--r--sound/core/seq/seq_timer.c24
-rw-r--r--sound/core/sgbuf.c62
-rw-r--r--sound/core/sound.c42
-rw-r--r--sound/core/sound_oss.c12
-rw-r--r--sound/core/timer.c39
-rw-r--r--sound/core/timer_compat.c9
-rw-r--r--sound/drivers/Kconfig91
-rw-r--r--sound/drivers/dummy.c41
-rw-r--r--sound/drivers/mtpav.c8
-rw-r--r--sound/drivers/opl3/opl3_lib.c6
-rw-r--r--sound/drivers/opl3/opl3_midi.c6
-rw-r--r--sound/drivers/opl3/opl3_oss.c15
-rw-r--r--sound/drivers/opl3/opl3_synth.c3
-rw-r--r--sound/drivers/opl4/opl4_synth.c2
-rw-r--r--sound/drivers/vx/vx_cmd.c3
-rw-r--r--sound/drivers/vx/vx_core.c25
-rw-r--r--sound/drivers/vx/vx_hwdep.c8
-rw-r--r--sound/drivers/vx/vx_mixer.c3
-rw-r--r--sound/drivers/vx/vx_pcm.c9
-rw-r--r--sound/drivers/vx/vx_uer.c6
-rw-r--r--sound/i2c/cs8427.c21
-rw-r--r--sound/i2c/i2c.c6
-rw-r--r--sound/i2c/l3/uda1341.c5
-rw-r--r--sound/i2c/other/ak4114.c3
-rw-r--r--sound/i2c/other/ak4117.c3
-rw-r--r--sound/i2c/other/ak4xxx-adda.c4
-rw-r--r--sound/i2c/other/tea575x-tuner.c25
-rw-r--r--sound/isa/Kconfig112
-rw-r--r--sound/isa/Makefile2
-rw-r--r--sound/isa/ad1816a/ad1816a.c4
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c6
-rw-r--r--sound/isa/ad1848/Makefile2
-rw-r--r--sound/isa/ad1848/ad1848.c17
-rw-r--r--sound/isa/ad1848/ad1848_lib.c1267
-rw-r--r--sound/isa/azt2320.c29
-rw-r--r--sound/isa/cmi8330.c106
-rw-r--r--sound/isa/cs423x/Makefile2
-rw-r--r--sound/isa/cs423x/cs4231.c18
-rw-r--r--sound/isa/cs423x/cs4231_lib.c1845
-rw-r--r--sound/isa/cs423x/cs4236.c52
-rw-r--r--sound/isa/cs423x/cs4236_lib.c349
-rw-r--r--sound/isa/es1688/es1688_lib.c3
-rw-r--r--sound/isa/gus/gus_main.c6
-rw-r--r--sound/isa/gus/gus_mixer.c6
-rw-r--r--sound/isa/gus/gus_pcm.c12
-rw-r--r--sound/isa/gus/gusmax.c50
-rw-r--r--sound/isa/gus/interwave.c67
-rw-r--r--sound/isa/opl3sa2.c99
-rw-r--r--sound/isa/opti9xx/miro.c28
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c1170
-rw-r--r--sound/isa/sb/Makefile2
-rw-r--r--sound/isa/sb/emu8000.c3
-rw-r--r--sound/isa/sb/emu8000_patch.c3
-rw-r--r--sound/isa/sb/sb16_csp.c31
-rw-r--r--sound/isa/sb/sb16_csp_codecs.h949
-rw-r--r--sound/isa/sb/sb16_main.c3
-rw-r--r--sound/isa/sb/sb8_main.c8
-rw-r--r--sound/isa/sb/sb_common.c3
-rw-r--r--sound/isa/sb/sb_mixer.c9
-rw-r--r--sound/isa/sc6000.c18
-rw-r--r--sound/isa/sgalaxy.c43
-rw-r--r--sound/isa/sscape.c63
-rw-r--r--sound/isa/wavefront/wavefront.c62
-rw-r--r--sound/isa/wavefront/wavefront_fx.c8
-rw-r--r--sound/isa/wavefront/wavefront_midi.c24
-rw-r--r--sound/isa/wavefront/wavefront_synth.c9
-rw-r--r--sound/isa/wss/Makefile10
-rw-r--r--sound/isa/wss/wss_lib.c2322
-rw-r--r--sound/mips/Kconfig27
-rw-r--r--sound/mips/Makefile4
-rw-r--r--sound/mips/ad1843.c561
-rw-r--r--sound/mips/au1x00.c7
-rw-r--r--sound/mips/hal2.c947
-rw-r--r--sound/mips/hal2.h (renamed from sound/oss/hal2.h)59
-rw-r--r--sound/mips/sgio2audio.c1006
-rw-r--r--sound/oss/Kconfig121
-rw-r--r--sound/oss/Makefile4
-rw-r--r--sound/oss/ac97_codec.c2
-rw-r--r--sound/oss/aedsp16.c8
-rw-r--r--sound/oss/dmasound/Kconfig1
-rw-r--r--sound/oss/dmasound/dmasound_core.c7
-rw-r--r--sound/oss/dmasound/dmasound_paula.c2
-rw-r--r--sound/oss/dmasound/dmasound_q40.c2
-rw-r--r--sound/oss/hal2.c1558
-rw-r--r--sound/oss/mpu401.c2
-rw-r--r--sound/oss/msnd.c2
-rw-r--r--sound/oss/msnd.h2
-rw-r--r--sound/oss/msnd_classic.h2
-rw-r--r--sound/oss/msnd_pinnacle.c5
-rw-r--r--sound/oss/msnd_pinnacle.h2
-rw-r--r--sound/oss/soundcard.c7
-rw-r--r--sound/oss/trident.c4654
-rw-r--r--sound/oss/trident.h358
-rw-r--r--sound/oss/vidc.c2
-rw-r--r--sound/oss/vidc_fill.S2
-rw-r--r--sound/oss/vwsnd.c2
-rw-r--r--sound/oss/waveartist.c2
-rw-r--r--sound/parisc/Kconfig13
-rw-r--r--sound/parisc/harmony.c3
-rw-r--r--sound/pci/Kconfig167
-rw-r--r--sound/pci/Makefile2
-rw-r--r--sound/pci/ac97/Makefile12
-rw-r--r--sound/pci/ac97/ac97_codec.c55
-rw-r--r--sound/pci/ac97/ac97_patch.c291
-rw-r--r--sound/pci/ad1889.c6
-rw-r--r--sound/pci/ak4531_codec.c (renamed from sound/pci/ac97/ak4531_codec.c)44
-rw-r--r--sound/pci/als4000.c391
-rw-r--r--sound/pci/atiixp.c13
-rw-r--r--sound/pci/atiixp_modem.c10
-rw-r--r--sound/pci/au88x0/au88x0.h7
-rw-r--r--sound/pci/au88x0/au88x0_core.c40
-rw-r--r--sound/pci/au88x0/au88x0_game.c2
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c14
-rw-r--r--sound/pci/azt3328.c1239
-rw-r--r--sound/pci/azt3328.h207
-rw-r--r--sound/pci/bt87x.c5
-rw-r--r--sound/pci/ca0106/ca0106_main.c6
-rw-r--r--sound/pci/ca0106/ca_midi.c20
-rw-r--r--sound/pci/cmipci.c9
-rw-r--r--sound/pci/cs4281.c13
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c91
-rw-r--r--sound/pci/cs46xx/dsp_spos.c51
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c115
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c3
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c8
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c9
-rw-r--r--sound/pci/echoaudio/echoaudio.c22
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c11
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c21
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c6
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c6
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c11
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c8
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c8
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c8
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c9
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c11
-rw-r--r--sound/pci/echoaudio/mia_dsp.c13
-rw-r--r--sound/pci/echoaudio/midi.c6
-rw-r--r--sound/pci/echoaudio/mona_dsp.c6
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c6
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c1
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c23
-rw-r--r--sound/pci/emu10k1/emu10k1x.c18
-rw-r--r--sound/pci/emu10k1/emufx.c6
-rw-r--r--sound/pci/emu10k1/emumixer.c13
-rw-r--r--sound/pci/emu10k1/emumpu401.c18
-rw-r--r--sound/pci/emu10k1/memory.c100
-rw-r--r--sound/pci/emu10k1/voice.c9
-rw-r--r--sound/pci/ens1370.c3
-rw-r--r--sound/pci/es1938.c3
-rw-r--r--sound/pci/es1968.c6
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_beep.c134
-rw-r--r--sound/pci/hda/hda_beep.h44
-rw-r--r--sound/pci/hda/hda_codec.c115
-rw-r--r--sound/pci/hda/hda_codec.h95
-rw-r--r--sound/pci/hda/hda_generic.c3
-rw-r--r--sound/pci/hda/hda_hwdep.c2
-rw-r--r--sound/pci/hda/hda_intel.c453
-rw-r--r--sound/pci/hda/hda_local.h24
-rw-r--r--sound/pci/hda/hda_patch.h2
-rw-r--r--sound/pci/hda/hda_proc.c30
-rw-r--r--sound/pci/hda/patch_analog.c134
-rw-r--r--sound/pci/hda/patch_atihdmi.c45
-rw-r--r--sound/pci/hda/patch_conexant.c33
-rw-r--r--sound/pci/hda/patch_nvhdmi.c164
-rw-r--r--sound/pci/hda/patch_realtek.c2504
-rw-r--r--sound/pci/hda/patch_sigmatel.c1086
-rw-r--r--sound/pci/hda/patch_via.c1407
-rw-r--r--sound/pci/ice1712/ak4xxx.c3
-rw-r--r--sound/pci/ice1712/aureon.c699
-rw-r--r--sound/pci/ice1712/delta.c2
-rw-r--r--sound/pci/ice1712/delta.h1
-rw-r--r--sound/pci/ice1712/envy24ht.h10
-rw-r--r--sound/pci/ice1712/ews.c9
-rw-r--r--sound/pci/ice1712/ice1712.c240
-rw-r--r--sound/pci/ice1712/ice1712.h50
-rw-r--r--sound/pci/ice1712/ice1724.c401
-rw-r--r--sound/pci/ice1712/juli.c47
-rw-r--r--sound/pci/ice1712/phase.c277
-rw-r--r--sound/pci/ice1712/phase.h8
-rw-r--r--sound/pci/ice1712/pontis.c5
-rw-r--r--sound/pci/ice1712/revo.c25
-rw-r--r--sound/pci/ice1712/wtm.c104
-rw-r--r--sound/pci/ice1712/wtm.h4
-rw-r--r--sound/pci/intel8x0.c50
-rw-r--r--sound/pci/intel8x0m.c3
-rw-r--r--sound/pci/korg1212/korg1212-firmware.h987
-rw-r--r--sound/pci/korg1212/korg1212.c27
-rw-r--r--sound/pci/maestro3.c238
-rw-r--r--sound/pci/mixart/mixart.c4
-rw-r--r--sound/pci/mixart/mixart_core.c18
-rw-r--r--sound/pci/mixart/mixart_hwdep.c21
-rw-r--r--sound/pci/mixart/mixart_mixer.c8
-rw-r--r--sound/pci/nm256/nm256.c19
-rw-r--r--sound/pci/oxygen/hifier.c50
-rw-r--r--sound/pci/oxygen/oxygen.c170
-rw-r--r--sound/pci/oxygen/oxygen.h107
-rw-r--r--sound/pci/oxygen/oxygen_io.c44
-rw-r--r--sound/pci/oxygen/oxygen_lib.c199
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c53
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c100
-rw-r--r--sound/pci/oxygen/virtuoso.c789
-rw-r--r--sound/pci/pcxhr/pcxhr.c10
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c49
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c17
-rw-r--r--sound/pci/riptide/riptide.c41
-rw-r--r--sound/pci/rme9652/hdsp.c25
-rw-r--r--sound/pci/rme9652/hdspm.c52
-rw-r--r--sound/pci/rme9652/rme9652.c23
-rw-r--r--sound/pci/sonicvibes.c10
-rw-r--r--sound/pci/trident/trident_main.c27
-rw-r--r--sound/pci/trident/trident_memory.c215
-rw-r--r--sound/pci/via82xx.c66
-rw-r--r--sound/pci/via82xx_modem.c8
-rw-r--r--sound/pci/vx222/vx222_ops.c14
-rw-r--r--sound/pci/ymfpci/ymfpci_image.h1565
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c97
-rw-r--r--sound/pcmcia/Kconfig15
-rw-r--r--sound/pcmcia/vx/vxp_ops.c5
-rw-r--r--sound/ppc/Kconfig26
-rw-r--r--sound/ppc/awacs.c73
-rw-r--r--sound/ppc/beep.c6
-rw-r--r--sound/ppc/daca.c2
-rw-r--r--sound/ppc/tumbler.c19
-rw-r--r--sound/sh/Kconfig16
-rw-r--r--sound/sh/aica.c11
-rw-r--r--sound/soc/Kconfig21
-rw-r--r--sound/soc/Makefile3
-rw-r--r--sound/soc/at32/Kconfig34
-rw-r--r--sound/soc/at32/Makefile11
-rw-r--r--sound/soc/at32/at32-pcm.c492
-rw-r--r--sound/soc/at32/at32-pcm.h79
-rw-r--r--sound/soc/at32/at32-ssc.c849
-rw-r--r--sound/soc/at32/at32-ssc.h59
-rw-r--r--sound/soc/at32/playpaq_wm8510.c519
-rw-r--r--sound/soc/at91/Kconfig19
-rw-r--r--sound/soc/at91/Makefile5
-rw-r--r--sound/soc/at91/at91-pcm.c10
-rw-r--r--sound/soc/at91/at91-pcm.h2
-rw-r--r--sound/soc/at91/at91-ssc.c24
-rw-r--r--sound/soc/at91/at91-ssc.h2
-rw-r--r--sound/soc/at91/eti_b1_wm8731.c358
-rw-r--r--sound/soc/au1x/Kconfig32
-rw-r--r--sound/soc/au1x/Makefile13
-rw-r--r--sound/soc/au1x/dbdma2.c421
-rw-r--r--sound/soc/au1x/psc-ac97.c387
-rw-r--r--sound/soc/au1x/psc-i2s.c414
-rw-r--r--sound/soc/au1x/psc.h53
-rw-r--r--sound/soc/au1x/sample-ac97.c144
-rw-r--r--sound/soc/blackfin/Kconfig101
-rw-r--r--sound/soc/blackfin/Makefile21
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c457
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.h29
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c406
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h36
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c113
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c240
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c288
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h29
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c311
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.h14
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c1032
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h194
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c186
-rw-r--r--sound/soc/codecs/Kconfig98
-rw-r--r--sound/soc/codecs/Makefile34
-rw-r--r--sound/soc/codecs/ac97.c34
-rw-r--r--sound/soc/codecs/ac97.h2
-rw-r--r--sound/soc/codecs/ad1980.c308
-rw-r--r--sound/soc/codecs/ad1980.h23
-rw-r--r--sound/soc/codecs/ad73311.c107
-rw-r--r--sound/soc/codecs/ad73311.h90
-rw-r--r--sound/soc/codecs/ak4535.c694
-rw-r--r--sound/soc/codecs/ak4535.h47
-rw-r--r--sound/soc/codecs/cs4270.c111
-rw-r--r--sound/soc/codecs/cs4270.h2
-rw-r--r--sound/soc/codecs/ssm2602.c775
-rw-r--r--sound/soc/codecs/ssm2602.h130
-rw-r--r--sound/soc/codecs/tlv320aic23.c714
-rw-r--r--sound/soc/codecs/tlv320aic23.h122
-rw-r--r--sound/soc/codecs/tlv320aic26.c520
-rw-r--r--sound/soc/codecs/tlv320aic26.h96
-rw-r--r--sound/soc/codecs/tlv320aic3x.c502
-rw-r--r--sound/soc/codecs/tlv320aic3x.h58
-rw-r--r--sound/soc/codecs/uda1380.c849
-rw-r--r--sound/soc/codecs/uda1380.h90
-rw-r--r--sound/soc/codecs/wm8510.c895
-rw-r--r--sound/soc/codecs/wm8510.h105
-rw-r--r--sound/soc/codecs/wm8580.c1053
-rw-r--r--sound/soc/codecs/wm8580.h42
-rw-r--r--sound/soc/codecs/wm8731.c263
-rw-r--r--sound/soc/codecs/wm8731.h4
-rw-r--r--sound/soc/codecs/wm8750.c272
-rw-r--r--sound/soc/codecs/wm8750.h4
-rw-r--r--sound/soc/codecs/wm8753.c370
-rw-r--r--sound/soc/codecs/wm8753.h7
-rw-r--r--sound/soc/codecs/wm8900.c1541
-rw-r--r--sound/soc/codecs/wm8900.h64
-rw-r--r--sound/soc/codecs/wm8903.c1813
-rw-r--r--sound/soc/codecs/wm8903.h1463
-rw-r--r--sound/soc/codecs/wm8971.c941
-rw-r--r--sound/soc/codecs/wm8971.h64
-rw-r--r--sound/soc/codecs/wm8990.c1635
-rw-r--r--sound/soc/codecs/wm8990.h843
-rw-r--r--sound/soc/codecs/wm9712.c67
-rw-r--r--sound/soc/codecs/wm9712.h2
-rw-r--r--sound/soc/codecs/wm9713.c116
-rw-r--r--sound/soc/codecs/wm9713.h2
-rw-r--r--sound/soc/davinci/Kconfig2
-rw-r--r--sound/soc/davinci/davinci-evm.c46
-rw-r--r--sound/soc/davinci/davinci-i2s.c20
-rw-r--r--sound/soc/davinci/davinci-i2s.h4
-rw-r--r--sound/soc/davinci/davinci-pcm.c4
-rw-r--r--sound/soc/davinci/davinci-pcm.h2
-rw-r--r--sound/soc/fsl/Kconfig12
-rw-r--r--sound/soc/fsl/Makefile5
-rw-r--r--sound/soc/fsl/fsl_dma.c244
-rw-r--r--sound/soc/fsl/fsl_dma.h2
-rw-r--r--sound/soc/fsl/fsl_ssi.c98
-rw-r--r--sound/soc/fsl/fsl_ssi.h4
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c884
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c98
-rw-r--r--sound/soc/fsl/soc-of-simple.c171
-rw-r--r--sound/soc/omap/Kconfig10
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/n810.c129
-rw-r--r--sound/soc/omap/omap-mcbsp.c201
-rw-r--r--sound/soc/omap/omap-mcbsp.h18
-rw-r--r--sound/soc/omap/omap-pcm.c8
-rw-r--r--sound/soc/omap/osk5912.c232
-rw-r--r--sound/soc/pxa/Kconfig15
-rw-r--r--sound/soc/pxa/Makefile3
-rw-r--r--sound/soc/pxa/corgi.c115
-rw-r--r--sound/soc/pxa/e800_wm9712.c6
-rw-r--r--sound/soc/pxa/em-x270.c102
-rw-r--r--sound/soc/pxa/poodle.c69
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c304
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h2
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c142
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h2
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c265
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.h15
-rw-r--r--sound/soc/pxa/spitz.c146
-rw-r--r--sound/soc/pxa/tosa.c85
-rw-r--r--sound/soc/s3c24xx/Kconfig4
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c320
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c23
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h2
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c25
-rw-r--r--sound/soc/s3c24xx/s3c24xx-ac97.h2
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c35
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.h2
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c12
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c3
-rw-r--r--sound/soc/sh/Kconfig5
-rw-r--r--sound/soc/sh/dma-sh7760.c2
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/sh/sh7760-ac97.c4
-rw-r--r--sound/soc/sh/ssi.c8
-rw-r--r--sound/soc/soc-core.c568
-rw-r--r--sound/soc/soc-dapm.c536
-rw-r--r--sound/sound_core.c86
-rw-r--r--sound/sparc/Kconfig17
-rw-r--r--sound/sparc/amd7930.c104
-rw-r--r--sound/sparc/cs4231.c202
-rw-r--r--sound/sparc/dbri.c108
-rw-r--r--sound/spi/Kconfig13
-rw-r--r--sound/synth/emux/emux.c8
-rw-r--r--sound/synth/emux/emux_nrpn.c8
-rw-r--r--sound/synth/emux/emux_oss.c42
-rw-r--r--sound/synth/emux/emux_seq.c15
-rw-r--r--sound/synth/emux/emux_synth.c47
-rw-r--r--sound/synth/util_mem.c10
-rw-r--r--sound/usb/Kconfig28
-rw-r--r--sound/usb/Makefile1
-rw-r--r--sound/usb/caiaq/caiaq-audio.c1
-rw-r--r--sound/usb/caiaq/caiaq-device.c12
-rw-r--r--sound/usb/caiaq/caiaq-device.h1
-rw-r--r--sound/usb/usbaudio.c50
-rw-r--r--sound/usb/usbaudio.h6
-rw-r--r--sound/usb/usbmidi.c48
-rw-r--r--sound/usb/usbmixer.c19
-rw-r--r--sound/usb/usbquirks.h114
-rw-r--r--sound/usb/usx2y/Makefile2
-rw-r--r--sound/usb/usx2y/us122l.c693
-rw-r--r--sound/usb/usx2y/us122l.h27
-rw-r--r--sound/usb/usx2y/usb_stream.c761
-rw-r--r--sound/usb/usx2y/usb_stream.h112
445 files changed, 46751 insertions, 23199 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index 4247406160e..200aca1faa7 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,11 +1,9 @@
# sound/Config.in
#
-menu "Sound"
- depends on HAS_IOMEM
-
-config SOUND
+menuconfig SOUND
tristate "Sound card support"
+ depends on HAS_IOMEM
help
If you have a sound card in your computer, i.e. if it can say more
than an occasional beep, say Y. Be sure to have all the information
@@ -28,22 +26,26 @@ config SOUND
and read <file:Documentation/sound/oss/README.modules>; the module
will be called soundcore.
+if SOUND
+
+config SOUND_OSS_CORE
+ bool
+ default n
+
source "sound/oss/dmasound/Kconfig"
if !M68K
-menu "Advanced Linux Sound Architecture"
- depends on SOUND!=n
-
-config SND
+menuconfig SND
tristate "Advanced Linux Sound Architecture"
- depends on SOUND
help
Say 'Y' or 'M' to enable ALSA (Advanced Linux Sound Architecture),
the new base sound system.
For more information, see <http://www.alsa-project.org/>
+if SND
+
source "sound/core/Kconfig"
source "sound/drivers/Kconfig"
@@ -58,9 +60,7 @@ source "sound/aoa/Kconfig"
source "sound/arm/Kconfig"
-if SPI
source "sound/spi/Kconfig"
-endif
source "sound/mips/Kconfig"
@@ -80,23 +80,25 @@ source "sound/parisc/Kconfig"
source "sound/soc/Kconfig"
-endmenu
-
-menu "Open Sound System"
- depends on SOUND!=n
+endif # SND
-config SOUND_PRIME
+menuconfig SOUND_PRIME
tristate "Open Sound System (DEPRECATED)"
- depends on SOUND
+ select SOUND_OSS_CORE
help
Say 'Y' or 'M' to enable Open Sound System drivers.
+if SOUND_PRIME
+
source "sound/oss/Kconfig"
-endmenu
+endif # SOUND_PRIME
+
+endif # !M68K
-endif
+endif # SOUND
+# AC97_BUS is used from both sound and ucb1400
config AC97_BUS
tristate
help
@@ -105,4 +107,3 @@ config AC97_BUS
sound although they're sharing the AC97 bus. Concerned drivers
should "select" this.
-endmenu
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index 5d5813cec4c..c081e18b954 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -1,18 +1,17 @@
-menu "Apple Onboard Audio driver"
- depends on SND!=n && PPC_PMAC
-
-config SND_AOA
+menuconfig SND_AOA
tristate "Apple Onboard Audio driver"
- depends on SND
+ depends on PPC_PMAC
select SND_PCM
---help---
This option enables the new driver for the various
Apple Onboard Audio components.
+if SND_AOA
+
source "sound/aoa/fabrics/Kconfig"
source "sound/aoa/codecs/Kconfig"
source "sound/aoa/soundbus/Kconfig"
-endmenu
+endif # SND_AOA
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index d5fbd6016e9..808eb11ebac 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,6 +1,5 @@
config SND_AOA_ONYX
tristate "support Onyx chip"
- depends on SND_AOA
select I2C
select I2C_POWERMAC
---help---
@@ -10,7 +9,6 @@ config SND_AOA_ONYX
#config SND_AOA_TOPAZ
# tristate "support Topaz chips"
-# depends on SND_AOA
# ---help---
# This option enables support for the Topaz (CS84xx)
# codec chips found in the latest Apple machines,
@@ -19,7 +17,6 @@ config SND_AOA_ONYX
config SND_AOA_TAS
tristate "support TAS chips"
- depends on SND_AOA
select I2C
select I2C_POWERMAC
---help---
@@ -29,7 +26,6 @@ config SND_AOA_TAS
config SND_AOA_TOONIE
tristate "support Toonie chip"
- depends on SND_AOA
---help---
This option enables support for the toonie codec
found in the Mac Mini. If you have a Mac Mini and
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
index 7a16a3331f7..6c515b2b8bb 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
+++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
@@ -654,15 +654,13 @@ static struct snd_kcontrol_new bass_control = {
static struct transfer_info tas_transfers[] = {
{
/* input */
- .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.transfer_in = 1,
},
{
/* output */
- .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.transfer_in = 0,
},
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig
index 50d7021ff67..3ca475a886b 100644
--- a/sound/aoa/fabrics/Kconfig
+++ b/sound/aoa/fabrics/Kconfig
@@ -1,6 +1,5 @@
config SND_AOA_FABRIC_LAYOUT
tristate "layout-id fabric"
- depends on SND_AOA
select SND_AOA_SOUNDBUS
select SND_AOA_SOUNDBUS_I2S
---help---
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index 7368b7ddfe0..839d1137b9b 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,6 +1,5 @@
config SND_AOA_SOUNDBUS
tristate "Apple Soundbus support"
- depends on SOUND
select SND_PCM
---help---
This option enables the generic driver for the soundbus
diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h
index 622cd37a011..a0f223c13f6 100644
--- a/sound/aoa/soundbus/soundbus.h
+++ b/sound/aoa/soundbus/soundbus.h
@@ -8,7 +8,7 @@
#ifndef __SOUNDBUS_H
#define __SOUNDBUS_H
-#include <asm/of_device.h>
+#include <linux/of_device.h>
#include <sound/pcm.h>
#include <linux/list.h>
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 2e4a5e0d16d..f8e6de48d81 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -1,11 +1,19 @@
# ALSA ARM drivers
-menu "ALSA ARM devices"
- depends on SND!=n && ARM
+menuconfig SND_ARM
+ bool "ARM sound devices"
+ depends on ARM
+ default y
+ help
+ Support for sound devices specific to ARM architectures.
+ Drivers that are implemented on ASoC can be found in
+ "ALSA for SoC audio support" section.
+
+if SND_ARM
config SND_SA11XX_UDA1341
tristate "SA11xx UDA1341TS driver (iPaq H3600)"
- depends on ARCH_SA1100 && SND && L3
+ depends on ARCH_SA1100 && L3
select SND_PCM
help
Say Y here if you have a Compaq iPaq H3x00 handheld computer
@@ -16,7 +24,7 @@ config SND_SA11XX_UDA1341
config SND_ARMAACI
tristate "ARM PrimeCell PL041 AC Link support"
- depends on SND && ARM_AMBA
+ depends on ARM_AMBA
select SND_PCM
select SND_AC97_CODEC
@@ -24,13 +32,23 @@ config SND_PXA2XX_PCM
tristate
select SND_PCM
+config SND_PXA2XX_LIB
+ tristate
+ select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
+
+config SND_PXA2XX_LIB_AC97
+ bool
+
config SND_PXA2XX_AC97
tristate "AC97 driver for the Intel PXA2xx chip"
- depends on ARCH_PXA && SND
+ depends on ARCH_PXA
select SND_PXA2XX_PCM
select SND_AC97_CODEC
+ select SND_PXA2XX_LIB
+ select SND_PXA2XX_LIB_AC97
help
Say Y or M if you want to support any AC97 codec attached to
the PXA2xx AC97 interface.
-endmenu
+endif # SND_ARM
+
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 4ef6dd00c6e..2054de11de8 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -11,5 +11,9 @@ snd-aaci-objs := aaci.o devdma.o
obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o
snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
+obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
+snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
+snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
+
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index b0a47449496..89096e811a4 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -999,7 +999,7 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, sizeof(struct aaci));
if (card == NULL)
- return ERR_PTR(-ENOMEM);
+ return NULL;
card->private_free = aaci_free_card;
@@ -1083,8 +1083,8 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
return ret;
aaci = aaci_init_card(dev);
- if (IS_ERR(aaci)) {
- ret = PTR_ERR(aaci);
+ if (!aaci) {
+ ret = -ENOMEM;
goto out;
}
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
new file mode 100644
index 00000000000..34c1d94f921
--- /dev/null
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -0,0 +1,384 @@
+/*
+ * Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c
+ * which contain:
+ *
+ * Author: Nicolas Pitre
+ * Created: Dec 02, 2004
+ * Copyright: MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <sound/ac97_codec.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-gpio.h>
+#include <mach/audio.h>
+
+static DEFINE_MUTEX(car_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
+static volatile long gsr_bits;
+static struct clk *ac97_clk;
+static struct clk *ac97conf_clk;
+
+/*
+ * Beware PXA27x bugs:
+ *
+ * o Slot 12 read from modem space will hang controller.
+ * o CDONE, SDONE interrupt fails after any slot 12 IO.
+ *
+ * We therefore have an hybrid approach for waiting on SDONE (interrupt or
+ * 1 jiffy timeout if interrupt never comes).
+ */
+
+unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ unsigned short val = -1;
+ volatile u32 *reg_addr;
+
+ mutex_lock(&car_mutex);
+
+ /* set up primary or secondary codec space */
+ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ else
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr += (reg >> 1);
+
+ /* start read access across the ac97 link */
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ val = *reg_addr;
+ if (reg == AC97_GPIO_STATUS)
+ goto out;
+ if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
+ !((GSR | gsr_bits) & GSR_SDONE)) {
+ printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
+ __func__, reg, GSR | gsr_bits);
+ val = -1;
+ goto out;
+ }
+
+ /* valid data now */
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ val = *reg_addr;
+ /* but we've just started another cycle... */
+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+
+out: mutex_unlock(&car_mutex);
+ return val;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
+
+void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ volatile u32 *reg_addr;
+
+ mutex_lock(&car_mutex);
+
+ /* set up primary or secondary codec space */
+ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ else
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr += (reg >> 1);
+
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ *reg_addr = val;
+ if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
+ !((GSR | gsr_bits) & GSR_CDONE))
+ printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
+ __func__, reg, GSR | gsr_bits);
+
+ mutex_unlock(&car_mutex);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
+
+#ifdef CONFIG_PXA25x
+static inline void pxa_ac97_warm_pxa25x(void)
+{
+ gsr_bits = 0;
+
+ GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
+ wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+}
+
+static inline void pxa_ac97_cold_pxa25x(void)
+{
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+
+ GCR = GCR_COLD_RST;
+ GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
+ wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+}
+#endif
+
+#ifdef CONFIG_PXA27x
+static inline void pxa_ac97_warm_pxa27x(void)
+{
+ gsr_bits = 0;
+
+ /* warm reset broken on Bulverde,
+ so manually keep AC97 reset high */
+ pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
+ udelay(10);
+ GCR |= GCR_WARM_RST;
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ udelay(500);
+}
+
+static inline void pxa_ac97_cold_pxa27x(void)
+{
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+
+ /* PXA27x Developers Manual section 13.5.2.2.1 */
+ clk_enable(ac97conf_clk);
+ udelay(5);
+ clk_disable(ac97conf_clk);
+ GCR = GCR_COLD_RST;
+ udelay(50);
+}
+#endif
+
+#ifdef CONFIG_PXA3xx
+static inline void pxa_ac97_warm_pxa3xx(void)
+{
+ int timeout = 100;
+
+ gsr_bits = 0;
+
+ /* Can't use interrupts */
+ GCR |= GCR_WARM_RST;
+ while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
+}
+
+static inline void pxa_ac97_cold_pxa3xx(void)
+{
+ int timeout = 1000;
+
+ /* Hold CLKBPB for 100us */
+ GCR = 0;
+ GCR = GCR_CLKBPB;
+ udelay(100);
+ GCR = 0;
+
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+
+ /* Can't use interrupts on PXA3xx */
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+
+ GCR = GCR_WARM_RST | GCR_COLD_RST;
+ while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(10);
+}
+#endif
+
+bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
+{
+#ifdef CONFIG_PXA25x
+ if (cpu_is_pxa25x())
+ pxa_ac97_warm_pxa25x();
+ else
+#endif
+#ifdef CONFIG_PXA27x
+ if (cpu_is_pxa27x())
+ pxa_ac97_warm_pxa27x();
+ else
+#endif
+#ifdef CONFIG_PXA3xx
+ if (cpu_is_pxa3xx())
+ pxa_ac97_warm_pxa3xx();
+ else
+#endif
+ BUG();
+
+ if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
+ printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
+ __func__, gsr_bits);
+
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
+
+bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
+{
+#ifdef CONFIG_PXA25x
+ if (cpu_is_pxa25x())
+ pxa_ac97_cold_pxa25x();
+ else
+#endif
+#ifdef CONFIG_PXA27x
+ if (cpu_is_pxa27x())
+ pxa_ac97_cold_pxa27x();
+ else
+#endif
+#ifdef CONFIG_PXA3xx
+ if (cpu_is_pxa3xx())
+ pxa_ac97_cold_pxa3xx();
+ else
+#endif
+ BUG();
+
+ if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
+ printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
+ __func__, gsr_bits);
+
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
+
+
+void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97)
+{
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset);
+
+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
+{
+ long status;
+
+ status = GSR;
+ if (status) {
+ GSR = status;
+ gsr_bits |= status;
+ wake_up(&gsr_wq);
+
+ /* Although we don't use those we still need to clear them
+ since they tend to spuriously trigger when MMC is used
+ (hardware bug? go figure)... */
+ if (cpu_is_pxa27x()) {
+ MISR = MISR_EOC;
+ PISR = PISR_EOC;
+ MCSR = MCSR_EOC;
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+#ifdef CONFIG_PM
+int pxa2xx_ac97_hw_suspend(void)
+{
+ GCR |= GCR_ACLINK_OFF;
+ clk_disable(ac97_clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend);
+
+int pxa2xx_ac97_hw_resume(void)
+{
+ if (cpu_is_pxa25x() || cpu_is_pxa27x()) {
+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+ }
+ if (cpu_is_pxa27x()) {
+ /* Use GPIO 113 as AC97 Reset on Bulverde */
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ }
+ clk_enable(ac97_clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
+#endif
+
+int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
+{
+ int ret;
+
+ ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
+ if (ret < 0)
+ goto err;
+
+ if (cpu_is_pxa25x() || cpu_is_pxa27x()) {
+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+ }
+
+ if (cpu_is_pxa27x()) {
+ /* Use GPIO 113 as AC97 Reset on Bulverde */
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
+ if (IS_ERR(ac97conf_clk)) {
+ ret = PTR_ERR(ac97conf_clk);
+ ac97conf_clk = NULL;
+ goto err_irq;
+ }
+ }
+
+ ac97_clk = clk_get(&dev->dev, "AC97CLK");
+ if (IS_ERR(ac97_clk)) {
+ ret = PTR_ERR(ac97_clk);
+ ac97_clk = NULL;
+ goto err_irq;
+ }
+
+ return clk_enable(ac97_clk);
+
+err_irq:
+ GCR |= GCR_ACLINK_OFF;
+ if (ac97conf_clk) {
+ clk_put(ac97conf_clk);
+ ac97conf_clk = NULL;
+ }
+ free_irq(IRQ_AC97, NULL);
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
+
+void pxa2xx_ac97_hw_remove(struct platform_device *dev)
+{
+ GCR |= GCR_ACLINK_OFF;
+ free_irq(IRQ_AC97, NULL);
+ if (ac97conf_clk) {
+ clk_put(ac97conf_clk);
+ ac97conf_clk = NULL;
+ }
+ clk_disable(ac97_clk);
+ clk_put(ac97_clk);
+ ac97_clk = NULL;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel/Marvell PXA sound library");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 5b3274b465e..c2635beb4c8 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -12,198 +12,27 @@
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/kernel.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#include <sound/pxa2xx-lib.h>
-#include <asm/irq.h>
-#include <linux/mutex.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/pxa2xx-gpio.h>
-#include <asm/arch/audio.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/audio.h>
#include "pxa2xx-pcm.h"
-
-static DEFINE_MUTEX(car_mutex);
-static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
-static volatile long gsr_bits;
-static struct clk *ac97_clk;
-#ifdef CONFIG_PXA27x
-static struct clk *ac97conf_clk;
-#endif
-
-/*
- * Beware PXA27x bugs:
- *
- * o Slot 12 read from modem space will hang controller.
- * o CDONE, SDONE interrupt fails after any slot 12 IO.
- *
- * We therefore have an hybrid approach for waiting on SDONE (interrupt or
- * 1 jiffy timeout if interrupt never comes).
- */
-
-static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- unsigned short val = -1;
- volatile u32 *reg_addr;
-
- mutex_lock(&car_mutex);
-
- /* set up primary or secondary codec space */
- reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
- reg_addr += (reg >> 1);
-
- /* start read access across the ac97 link */
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- val = *reg_addr;
- if (reg == AC97_GPIO_STATUS)
- goto out;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_SDONE)) {
- printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
- val = -1;
- goto out;
- }
-
- /* valid data now */
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- val = *reg_addr;
- /* but we've just started another cycle... */
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out: mutex_unlock(&car_mutex);
- return val;
-}
-
-static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
- volatile u32 *reg_addr;
-
- mutex_lock(&car_mutex);
-
- /* set up primary or secondary codec space */
- reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
- reg_addr += (reg >> 1);
-
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- *reg_addr = val;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_CDONE))
- printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
-
- mutex_unlock(&car_mutex);
-}
-
static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
{
- /* First, try cold reset */
-#ifdef CONFIG_PXA3xx
- int timeout;
-
- /* Hold CLKBPB for 100us */
- GCR = 0;
- GCR = GCR_CLKBPB;
- udelay(100);
- GCR = 0;
-#endif
-
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
-
- gsr_bits = 0;
-#ifdef CONFIG_PXA27x
- /* PXA27x Developers Manual section 13.5.2.2.1 */
- clk_enable(ac97conf_clk);
- udelay(5);
- clk_disable(ac97conf_clk);
- GCR = GCR_COLD_RST;
- udelay(50);
-#elif defined(CONFIG_PXA3xx)
- timeout = 1000;
- /* Can't use interrupts on PXA3xx */
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-
- GCR = GCR_WARM_RST | GCR_COLD_RST;
- while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(10);
-#else
- GCR = GCR_COLD_RST;
- GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
- if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
- printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
- __func__, gsr_bits);
-
- /* let's try warm reset */
- gsr_bits = 0;
-#ifdef CONFIG_PXA27x
- /* warm reset broken on Bulverde,
- so manually keep AC97 reset high */
- pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
- udelay(10);
- GCR |= GCR_WARM_RST;
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
- udelay(500);
-#elif defined(CONFIG_PXA3xx)
- timeout = 100;
- /* Can't use interrupts */
- GCR |= GCR_WARM_RST;
- while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(1);
-#else
- GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
- if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
- printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
- __func__, gsr_bits);
- }
-
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
- GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
-}
-
-static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
-{
- long status;
-
- status = GSR;
- if (status) {
- GSR = status;
- gsr_bits |= status;
- wake_up(&gsr_wq);
-
-#ifdef CONFIG_PXA27x
- /* Although we don't use those we still need to clear them
- since they tend to spuriously trigger when MMC is used
- (hardware bug? go figure)... */
- MISR = MISR_EOC;
- PISR = PISR_EOC;
- MCSR = MCSR_EOC;
-#endif
-
- return IRQ_HANDLED;
+ if (!pxa2xx_ac97_try_cold_reset(ac97)) {
+ pxa2xx_ac97_try_warm_reset(ac97);
}
- return IRQ_NONE;
+ pxa2xx_ac97_finish_reset(ac97);
}
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
@@ -215,7 +44,7 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {
.name = "AC97 PCM out",
.dev_addr = __PREG(PCDR),
- .drcmr = &DRCMRTXPCDR,
+ .drcmr = &DRCMR(12),
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST32 | DCMD_WIDTH4,
};
@@ -223,7 +52,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = {
.name = "AC97 PCM in",
.dev_addr = __PREG(PCDR),
- .drcmr = &DRCMRRXPCDR,
+ .drcmr = &DRCMR(11),
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST32 | DCMD_WIDTH4,
};
@@ -288,17 +117,19 @@ static int pxa2xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)
snd_ac97_suspend(pxa2xx_ac97_ac97);
if (platform_ops && platform_ops->suspend)
platform_ops->suspend(platform_ops->priv);
- GCR |= GCR_ACLINK_OFF;
- clk_disable(ac97_clk);
- return 0;
+ return pxa2xx_ac97_hw_suspend();
}
static int pxa2xx_ac97_do_resume(struct snd_card *card)
{
pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+ int rc;
+
+ rc = pxa2xx_ac97_hw_resume();
+ if (rc)
+ return rc;
- clk_enable(ac97_clk);
if (platform_ops && platform_ops->resume)
platform_ops->resume(platform_ops->priv);
snd_ac97_resume(pxa2xx_ac97_ac97);
@@ -354,40 +185,17 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
if (ret)
goto err;
- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
- if (ret < 0)
- goto err;
-
- pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
- pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
- pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
- pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
-#ifdef CONFIG_PXA27x
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
- ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
- if (IS_ERR(ac97conf_clk)) {
- ret = PTR_ERR(ac97conf_clk);
- ac97conf_clk = NULL;
- goto err;
- }
-#endif
-
- ac97_clk = clk_get(&dev->dev, "AC97CLK");
- if (IS_ERR(ac97_clk)) {
- ret = PTR_ERR(ac97_clk);
- ac97_clk = NULL;
+ ret = pxa2xx_ac97_hw_probe(dev);
+ if (ret)
goto err;
- }
- clk_enable(ac97_clk);
ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
if (ret)
- goto err;
+ goto err_remove;
memset(&ac97_template, 0, sizeof(ac97_template));
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
if (ret)
- goto err;
+ goto err_remove;
snprintf(card->shortname, sizeof(card->shortname),
"%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
@@ -401,22 +209,11 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
return 0;
}
- err:
+err_remove:
+ pxa2xx_ac97_hw_remove(dev);
+err:
if (card)
snd_card_free(card);
- if (ac97_clk) {
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
- clk_disable(ac97_clk);
- clk_put(ac97_clk);
- ac97_clk = NULL;
- }
-#ifdef CONFIG_PXA27x
- if (ac97conf_clk) {
- clk_put(ac97conf_clk);
- ac97conf_clk = NULL;
- }
-#endif
return ret;
}
@@ -427,15 +224,7 @@ static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)
if (card) {
snd_card_free(card);
platform_set_drvdata(dev, NULL);
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
- clk_disable(ac97_clk);
- clk_put(ac97_clk);
- ac97_clk = NULL;
-#ifdef CONFIG_PXA27x
- clk_put(ac97conf_clk);
- ac97conf_clk = NULL;
-#endif
+ pxa2xx_ac97_hw_remove(dev);
}
return 0;
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
new file mode 100644
index 00000000000..1c93eb77cb9
--- /dev/null
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -0,0 +1,278 @@
+/*
+ * 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/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <asm/dma.h>
+#include <mach/pxa-regs.h>
+
+#include "pxa2xx-pcm.h"
+
+static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192 - 32,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 32,
+};
+
+int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *rtd = runtime->private_data;
+ size_t totsize = params_buffer_bytes(params);
+ size_t period = params_period_bytes(params);
+ pxa_dma_desc *dma_desc;
+ dma_addr_t dma_buff_phys, next_desc_phys;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = totsize;
+
+ dma_desc = rtd->dma_desc_array;
+ next_desc_phys = rtd->dma_desc_array_phys;
+ dma_buff_phys = runtime->dma_addr;
+ do {
+ next_desc_phys += sizeof(pxa_dma_desc);
+ dma_desc->ddadr = next_desc_phys;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_desc->dsadr = dma_buff_phys;
+ dma_desc->dtadr = rtd->params->dev_addr;
+ } else {
+ dma_desc->dsadr = rtd->params->dev_addr;
+ dma_desc->dtadr = dma_buff_phys;
+ }
+ if (period > totsize)
+ period = totsize;
+ dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
+ dma_desc++;
+ dma_buff_phys += period;
+ } while (totsize -= period);
+ dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
+
+int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+
+ if (rtd && rtd->params)
+ *rtd->params->drcmr = 0;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
+
+int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+ DCSR(prtd->dma_ch) = DCSR_RUN;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ DCSR(prtd->dma_ch) |= DCSR_RUN;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+ DCSR(prtd->dma_ch) |= DCSR_RUN;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_trigger);
+
+snd_pcm_uframes_t
+pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
+
+ dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
+ snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+ return x;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_pointer);
+
+int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+
+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+ DCSR(prtd->dma_ch) = 0;
+ DCMD(prtd->dma_ch) = 0;
+ *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
+
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
+
+void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+ int dcsr;
+
+ dcsr = DCSR(dma_ch);
+ DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
+
+ if (dcsr & DCSR_ENDINTR) {
+ snd_pcm_period_elapsed(substream);
+ } else {
+ printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+ rtd->params->name, dma_ch, dcsr);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ }
+}
+EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
+
+int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *rtd;
+ int ret;
+
+ runtime->hw = pxa2xx_pcm_hardware;
+
+ /*
+ * For mysterious reasons (and despite what the manual says)
+ * playback samples are lost if the DMA count is not a multiple
+ * of the DMA burst size. Let's add a rule to enforce that.
+ */
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ ret = -ENOMEM;
+ rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+ if (!rtd)
+ goto out;
+ rtd->dma_desc_array =
+ dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+ &rtd->dma_desc_array_phys, GFP_KERNEL);
+ if (!rtd->dma_desc_array)
+ goto err1;
+
+ runtime->private_data = rtd;
+ return 0;
+
+ err1:
+ kfree(rtd);
+ out:
+ return ret;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_open);
+
+int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *rtd = runtime->private_data;
+
+ dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+ rtd->dma_desc_array, rtd->dma_desc_array_phys);
+ kfree(rtd);
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_close);
+
+int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+EXPORT_SYMBOL(pxa2xx_pcm_mmap);
+
+int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ return 0;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
+
+void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx sound library");
+MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index 0ede9e4656a..535704f7749 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -10,183 +10,20 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-
#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include <asm/dma.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
+#include <sound/pxa2xx-lib.h>
#include "pxa2xx-pcm.h"
-
-static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .period_bytes_min = 32,
- .period_bytes_max = 8192 - 32,
- .periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
- .buffer_bytes_max = 128 * 1024,
- .fifo_size = 32,
-};
-
-struct pxa2xx_runtime_data {
- int dma_ch;
- struct pxa2xx_pcm_dma_params *params;
- pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
-};
-
-static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- size_t totsize = params_buffer_bytes(params);
- size_t period = params_period_bytes(params);
- pxa_dma_desc *dma_desc;
- dma_addr_t dma_buff_phys, next_desc_phys;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
-
- dma_desc = rtd->dma_desc_array;
- next_desc_phys = rtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- dma_desc->ddadr = next_desc_phys;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = rtd->params->dev_addr;
- } else {
- dma_desc->dsadr = rtd->params->dev_addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
-
- return 0;
-}
-
-static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
- *rtd->params->drcmr = 0;
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- DCSR(rtd->dma_ch) &= ~DCSR_RUN;
- DCSR(rtd->dma_ch) = 0;
- DCMD(rtd->dma_ch) = 0;
- *rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD;
+ __pxa2xx_pcm_prepare(substream);
return client->prepare(substream);
}
-static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys;
- DCSR(rtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(rtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DCSR(rtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- int dcsr;
-
- dcsr = DCSR(dma_ch);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- if (dcsr & DCSR_ENDINTR) {
- snd_pcm_period_elapsed(substream);
- } else {
- printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
- rtd->params->name, dma_ch, dcsr );
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- }
-}
-
-static snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
- if (x == runtime->buffer_size)
- x = 0;
- return x;
-}
-
-static int
-pxa2xx_pcm_hw_rule_mult32(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
-{
- struct snd_interval *i = hw_param_interval(params, rule->var);
- int changed = 0;
-
- if (i->min & 31) {
- i->min = (i->min & ~31) + 32;
- i->openmin = 0;
- changed = 1;
- }
-
- if (i->max & 31) {
- i->max &= ~31;
- i->openmax = 0;
- changed = 1;
- }
-
- return changed;
-}
-
static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
@@ -194,33 +31,11 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
struct pxa2xx_runtime_data *rtd;
int ret;
- runtime->hw = pxa2xx_pcm_hardware;
-
- /*
- * For mysterious reasons (and despite what the manual says)
- * playback samples are lost if the DMA count is not a multiple
- * of the DMA burst size. Let's add a rule to enforce that.
- */
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- pxa2xx_pcm_hw_rule_mult32, NULL,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
- if (ret)
- goto out;
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- pxa2xx_pcm_hw_rule_mult32, NULL,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+ ret = __pxa2xx_pcm_open(substream);
if (ret)
goto out;
- ret = -ENOMEM;
- rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
- if (!rtd)
- goto out;
- rtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &rtd->dma_desc_array_phys, GFP_KERNEL);
- if (!rtd->dma_desc_array)
- goto err1;
+ rtd = runtime->private_data;
rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
client->playback_params : client->capture_params;
@@ -230,17 +45,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
goto err2;
rtd->dma_ch = ret;
- runtime->private_data = rtd;
ret = client->startup(substream);
if (!ret)
goto out;
pxa_free_dma(rtd->dma_ch);
err2:
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- err1:
- kfree(rtd);
+ __pxa2xx_pcm_close(substream);
out:
return ret;
}
@@ -252,69 +63,22 @@ static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
pxa_free_dma(rtd->dma_ch);
client->shutdown(substream);
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
-}
-static int
-pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
+ return __pxa2xx_pcm_close(substream);
}
static struct snd_pcm_ops pxa2xx_pcm_ops = {
.open = pxa2xx_pcm_open,
.close = pxa2xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
- .hw_params = pxa2xx_pcm_hw_params,
- .hw_free = pxa2xx_pcm_hw_free,
+ .hw_params = __pxa2xx_pcm_hw_params,
+ .hw_free = __pxa2xx_pcm_hw_free,
.prepare = pxa2xx_pcm_prepare,
.trigger = pxa2xx_pcm_trigger,
.pointer = pxa2xx_pcm_pointer,
.mmap = pxa2xx_pcm_mmap,
};
-static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
-}
-
-static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
static u64 pxa2xx_pcm_dmamask = 0xffffffff;
int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client,
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
index b79f1e80378..5c4a4d38a08 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -9,14 +9,15 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <asm/dma.h>
-struct pxa2xx_pcm_dma_params {
- char *name; /* stream identifier */
- u32 dcmd; /* DMA descriptor dcmd field */
- volatile u32 *drcmr; /* the DMA request channel to use */
- u32 dev_addr; /* device physical address for DMA */
+struct pxa2xx_runtime_data {
+ int dma_ch;
+ struct pxa2xx_pcm_dma_params *params;
+ pxa_dma_desc *dma_desc_array;
+ dma_addr_t dma_desc_array_phys;
};
-
+
struct pxa2xx_pcm_client {
struct pxa2xx_pcm_dma_params *playback_params;
struct pxa2xx_pcm_dma_params *capture_params;
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
index 0eff33ca0f7..1dcd51d81d1 100644
--- a/sound/arm/sa11xx-uda1341.c
+++ b/sound/arm/sa11xx-uda1341.c
@@ -21,8 +21,6 @@
* merged HAL layer (patches from Brian)
*/
-/* $Id: sa11xx-uda1341.c,v 1.27 2005/12/07 09:13:42 cladisch Exp $ */
-
/***************************************************************************************************
*
* To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
@@ -73,8 +71,8 @@
#include <linux/pm.h>
#endif
-#include <asm/hardware.h>
-#include <asm/arch/h3600.h>
+#include <mach/hardware.h>
+#include <mach/h3600.h>
#include <asm/mach-types.h>
#include <asm/dma.h>
@@ -444,7 +442,8 @@ static void audio_process_dma(struct audio_stream *s)
/* we are requested to process synchronization DMA transfer */
if (s->tx_spin) {
- snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return);
+ if (snd_BUG_ON(s->stream_id != SNDRV_PCM_STREAM_PLAYBACK))
+ return;
/* fill the xmit dma buffers and return */
#ifdef HH_VERSION
sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
@@ -474,7 +473,7 @@ static void audio_process_dma(struct audio_stream *s)
continue; /* special case */
} else {
offset = dma_size * s->period;
- snd_assert(dma_size <= DMA_BUF_SIZE, );
+ snd_BUG_ON(dma_size > DMA_BUF_SIZE);
}
#ifdef HH_VERSION
ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
@@ -881,7 +880,7 @@ void snd_sa11xx_uda1341_free(struct snd_card *card)
audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
}
-static int __init sa11xx_uda1341_probe(struct platform_device *devptr)
+static int __devinit sa11xx_uda1341_probe(struct platform_device *devptr)
{
int err;
struct snd_card *card;
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index a8d71c6c8e7..66348c92f88 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,24 +1,25 @@
# ALSA soundcard-configuration
config SND_TIMER
tristate
- depends on SND
config SND_PCM
tristate
select SND_TIMER
- depends on SND
config SND_HWDEP
tristate
- depends on SND
config SND_RAWMIDI
tristate
- depends on SND
+
+# To be effective this also requires INPUT - users should say:
+# select SND_JACK if INPUT=y || INPUT=SND
+# to avoid having to force INPUT on.
+config SND_JACK
+ bool
config SND_SEQUENCER
tristate "Sequencer support"
- depends on SND
select SND_TIMER
help
Say Y or M to enable MIDI sequencer and router support. This
@@ -43,12 +44,11 @@ config SND_SEQ_DUMMY
will be called snd-seq-dummy.
config SND_OSSEMUL
+ select SOUND_OSS_CORE
bool
- depends on SND
config SND_MIXER_OSS
tristate "OSS Mixer API"
- depends on SND
select SND_OSSEMUL
help
To enable OSS mixer API emulation (/dev/mixer*), say Y here
@@ -61,7 +61,6 @@ config SND_MIXER_OSS
config SND_PCM_OSS
tristate "OSS PCM (digital audio) API"
- depends on SND
select SND_OSSEMUL
select SND_PCM
help
@@ -84,7 +83,7 @@ config SND_PCM_OSS_PLUGINS
config SND_SEQUENCER_OSS
bool "OSS Sequencer API"
- depends on SND && SND_SEQUENCER
+ depends on SND_SEQUENCER
select SND_OSSEMUL
help
Say Y here to enable OSS sequencer emulation (both
@@ -98,7 +97,7 @@ config SND_SEQUENCER_OSS
config SND_RTCTIMER
tristate "RTC Timer support"
- depends on SND && RTC
+ depends on RTC
select SND_TIMER
help
Say Y here to enable RTC timer support for ALSA. ALSA uses
@@ -109,6 +108,9 @@ config SND_RTCTIMER
To compile this driver as a module, choose M here: the module
will be called snd-rtctimer.
+ Note that this option is exclusive with the new RTC drivers
+ (CONFIG_RTC_CLASS) since this requires the old API.
+
config SND_SEQ_RTCTIMER_DEFAULT
bool "Use RTC as default sequencer timer"
depends on SND_RTCTIMER && SND_SEQUENCER
@@ -123,7 +125,6 @@ config SND_SEQ_RTCTIMER_DEFAULT
config SND_DYNAMIC_MINORS
bool "Dynamic device file minor numbers"
- depends on SND
help
If you say Y here, the minor numbers of ALSA device files in
/dev/snd/ are allocated dynamically. This allows you to have
@@ -134,7 +135,6 @@ config SND_DYNAMIC_MINORS
config SND_SUPPORT_OLD_API
bool "Support old ALSA API"
- depends on SND
default y
help
Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
@@ -142,7 +142,7 @@ config SND_SUPPORT_OLD_API
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
- depends on SND && PROC_FS
+ depends on PROC_FS
default y
help
Say Y here to include code for verbose procfs contents (provides
@@ -151,7 +151,6 @@ config SND_VERBOSE_PROCFS
config SND_VERBOSE_PRINTK
bool "Verbose printk"
- depends on SND
help
Say Y here to enable verbose log messages. These messages
will help to identify source file and position containing
@@ -161,16 +160,17 @@ config SND_VERBOSE_PRINTK
config SND_DEBUG
bool "Debug"
- depends on SND
help
Say Y here to enable ALSA debug code.
-config SND_DEBUG_DETECT
- bool "Debug detection"
+config SND_DEBUG_VERBOSE
+ bool "More verbose debug"
depends on SND_DEBUG
help
- Say Y here to enable extra-verbose log messages printed when
- detecting devices.
+ Say Y here to enable extra-verbose debugging messages.
+
+ Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages.
+ So, say Y only if you are ready to be annoyed.
config SND_PCM_XRUN_DEBUG
bool "Enable PCM ring buffer overrun/underrun debugging"
@@ -184,4 +184,3 @@ config SND_PCM_XRUN_DEBUG
config SND_VMASTER
bool
- depends on SND
diff --git a/sound/core/Makefile b/sound/core/Makefile
index da8e685eef9..d57125a5687 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -7,6 +7,7 @@ snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
snd-$(CONFIG_ISA_DMA_API) += isadma.o
snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
+snd-$(CONFIG_SND_JACK) += jack.o
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o
diff --git a/sound/core/control.c b/sound/core/control.c
index 01a1a5af47b..6d71f9a7ccb 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -139,7 +139,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_file *ctl;
struct snd_kctl_event *ev;
- snd_assert(card != NULL && id != NULL, return);
+ if (snd_BUG_ON(!card || !id))
+ return;
read_lock(&card->ctl_files_rwlock);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
card->mixer_oss_change_count++;
@@ -188,8 +189,8 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
struct snd_kcontrol *kctl;
unsigned int idx;
- snd_assert(control != NULL, return NULL);
- snd_assert(control->count > 0, return NULL);
+ if (snd_BUG_ON(!control || !control->count))
+ return NULL;
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
if (kctl == NULL) {
snd_printk(KERN_ERR "Cannot allocate control instance\n");
@@ -218,8 +219,8 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
struct snd_kcontrol kctl;
unsigned int access;
- snd_assert(ncontrol != NULL, return NULL);
- snd_assert(ncontrol->info != NULL, return NULL);
+ if (snd_BUG_ON(!ncontrol || !ncontrol->info))
+ return NULL;
memset(&kctl, 0, sizeof(kctl));
kctl.id.iface = ncontrol->iface;
kctl.id.device = ncontrol->device;
@@ -315,8 +316,8 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
if (! kcontrol)
return err;
- snd_assert(card != NULL, goto error);
- snd_assert(kcontrol->info != NULL, goto error);
+ if (snd_BUG_ON(!card || !kcontrol->info))
+ goto error;
id = kcontrol->id;
down_write(&card->controls_rwsem);
if (snd_ctl_find_id(card, &id)) {
@@ -367,7 +368,8 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
struct snd_ctl_elem_id id;
unsigned int idx;
- snd_assert(card != NULL && kcontrol != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card || !kcontrol))
+ return -EINVAL;
list_del(&kcontrol->list);
card->controls_count -= kcontrol->count;
id = kcontrol->id;
@@ -487,7 +489,8 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numi
{
struct snd_kcontrol *kctl;
- snd_assert(card != NULL && numid != 0, return NULL);
+ if (snd_BUG_ON(!card || !numid))
+ return NULL;
list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
return kctl;
@@ -514,7 +517,8 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
{
struct snd_kcontrol *kctl;
- snd_assert(card != NULL && id != NULL, return NULL);
+ if (snd_BUG_ON(!card || !id))
+ return NULL;
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
list_for_each_entry(kctl, &card->controls, list) {
@@ -647,7 +651,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
#endif
result = kctl->info(kctl, info);
if (result >= 0) {
- snd_assert(info->access == 0, );
+ snd_BUG_ON(info->access);
index_offset = snd_ctl_get_ioff(kctl, &info->id);
vd = &kctl->vd[index_offset];
snd_ctl_build_ioff(&info->id, kctl, index_offset);
@@ -684,7 +688,8 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
return result;
}
-int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_read(struct snd_card *card,
+ struct snd_ctl_elem_value *control)
{
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
@@ -734,8 +739,8 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
return result;
}
-int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
- struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
+ struct snd_ctl_elem_value *control)
{
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
@@ -1159,7 +1164,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
ctl = file->private_data;
card = ctl->card;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
@@ -1221,7 +1227,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
ssize_t result = 0;
ctl = file->private_data;
- snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!ctl || !ctl->card))
+ return -ENXIO;
if (!ctl->subscribed)
return -EBADFD;
if (count < sizeof(struct snd_ctl_event))
@@ -1327,7 +1334,8 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
{
struct snd_kctl_ioctl *p;
- snd_assert(fcn != NULL, return -EINVAL);
+ if (snd_BUG_ON(!fcn))
+ return -EINVAL;
down_write(&snd_ioctl_rwsem);
list_for_each_entry(p, lists, list) {
if (p->fioctl == fcn) {
@@ -1403,9 +1411,11 @@ static int snd_ctl_dev_register(struct snd_device *device)
int err, cardnum;
char name[16];
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
cardnum = card->number;
- snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
+ return -ENXIO;
sprintf(name, "controlC%i", cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
@@ -1422,16 +1432,18 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
struct snd_ctl_file *ctl;
int err, cardnum;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
cardnum = card->number;
- snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
+ return -ENXIO;
- down_read(&card->controls_rwsem);
+ read_lock(&card->ctl_files_rwlock);
list_for_each_entry(ctl, &card->ctl_files, list) {
wake_up(&ctl->change_sleep);
kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
}
- up_read(&card->controls_rwsem);
+ read_unlock(&card->ctl_files_rwlock);
if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
card, -1)) < 0)
@@ -1468,7 +1480,8 @@ int snd_ctl_create(struct snd_card *card)
.dev_disconnect = snd_ctl_dev_disconnect,
};
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 6101259ad86..368dc9c4aef 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -398,7 +398,8 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
int err;
ctl = file->private_data;
- snd_assert(ctl && ctl->card, return -ENXIO);
+ if (snd_BUG_ON(!ctl || !ctl->card))
+ return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
diff --git a/sound/core/device.c b/sound/core/device.c
index 202dac0e4d8..c58d8227254 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -45,9 +45,8 @@ int snd_device_new(struct snd_card *card, snd_device_type_t type,
{
struct snd_device *dev;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
- snd_assert(ops != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card || !device_data || !ops))
+ return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
snd_printk(KERN_ERR "Cannot allocate device\n");
@@ -80,8 +79,8 @@ int snd_device_free(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card || !device_data))
+ return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
if (dev->device_data != device_data)
continue;
@@ -123,8 +122,8 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card || !device_data))
+ return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
if (dev->device_data != device_data)
continue;
@@ -159,8 +158,8 @@ int snd_device_register(struct snd_card *card, void *device_data)
struct snd_device *dev;
int err;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card || !device_data))
+ return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
if (dev->device_data != device_data)
continue;
@@ -188,7 +187,8 @@ int snd_device_register_all(struct snd_card *card)
struct snd_device *dev;
int err;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
if ((err = dev->ops->dev_register(dev)) < 0)
@@ -208,7 +208,8 @@ int snd_device_disconnect_all(struct snd_card *card)
struct snd_device *dev;
int err = 0;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
if (snd_device_disconnect(card, dev->device_data) < 0)
err = -ENXIO;
@@ -226,7 +227,8 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
int err;
unsigned int range_low, range_high;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
__again:
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 6d6589f9389..195cafc5a55 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -353,9 +353,10 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
.dev_disconnect = snd_hwdep_dev_disconnect,
};
- snd_assert(rhwdep != NULL, return -EINVAL);
- *rhwdep = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rhwdep)
+ *rhwdep = NULL;
hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
if (hwdep == NULL) {
snd_printk(KERN_ERR "hwdep: cannot allocate\n");
@@ -374,13 +375,15 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
}
init_waitqueue_head(&hwdep->open_wait);
mutex_init(&hwdep->open_mutex);
- *rhwdep = hwdep;
+ if (rhwdep)
+ *rhwdep = hwdep;
return 0;
}
static int snd_hwdep_free(struct snd_hwdep *hwdep)
{
- snd_assert(hwdep != NULL, return -ENXIO);
+ if (!hwdep)
+ return 0;
if (hwdep->private_free)
hwdep->private_free(hwdep);
kfree(hwdep);
@@ -440,7 +443,8 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
{
struct snd_hwdep *hwdep = device->device_data;
- snd_assert(hwdep != NULL, return -ENXIO);
+ if (snd_BUG_ON(!hwdep))
+ return -ENXIO;
mutex_lock(&register_mutex);
if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
mutex_unlock(&register_mutex);
diff --git a/sound/core/info.c b/sound/core/info.c
index cb5ead3e202..527b207462b 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/time.h>
+#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
#include <sound/core.h>
@@ -216,7 +217,8 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
loff_t pos;
data = file->private_data;
- snd_assert(data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!data))
+ return -ENXIO;
pos = *offset;
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
return -EIO;
@@ -257,7 +259,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
loff_t pos;
data = file->private_data;
- snd_assert(data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!data))
+ return -ENXIO;
entry = data->entry;
pos = *offset;
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
@@ -613,7 +616,8 @@ int snd_info_card_create(struct snd_card *card)
char str[8];
struct snd_info_entry *entry;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
sprintf(str, "card%i", card->number);
if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
@@ -635,7 +639,8 @@ int snd_info_card_register(struct snd_card *card)
{
struct proc_dir_entry *p;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
if (!strcmp(card->id, card->proc_root->name))
return 0;
@@ -653,7 +658,8 @@ int snd_info_card_register(struct snd_card *card)
*/
void snd_info_card_disconnect(struct snd_card *card)
{
- snd_assert(card != NULL, return);
+ if (!card)
+ return;
mutex_lock(&info_mutex);
if (card->proc_root_link) {
snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
@@ -670,7 +676,8 @@ void snd_info_card_disconnect(struct snd_card *card)
*/
int snd_info_card_free(struct snd_card *card)
{
- snd_assert(card != NULL, return -ENXIO);
+ if (!card)
+ return 0;
snd_info_free_entry(card->proc_root);
card->proc_root = NULL;
return 0;
@@ -848,7 +855,7 @@ static void snd_info_disconnect(struct snd_info_entry *entry)
return;
list_del_init(&entry->list);
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- snd_assert(root, return);
+ snd_BUG_ON(!root);
snd_remove_proc_entry(root, entry->p);
entry->p = NULL;
}
@@ -946,7 +953,8 @@ int snd_info_register(struct snd_info_entry * entry)
{
struct proc_dir_entry *root, *p = NULL;
- snd_assert(entry != NULL, return -ENXIO);
+ if (snd_BUG_ON(!entry))
+ return -ENXIO;
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
mutex_lock(&info_mutex);
p = snd_create_proc_entry(entry->name, entry->mode, root);
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index e35789a9275..e4af138d651 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -43,8 +43,10 @@ int snd_oss_info_register(int dev, int num, char *string)
{
char *x;
- snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO);
- snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO);
+ if (snd_BUG_ON(dev < 0 || dev >= SNDRV_OSS_INFO_DEV_COUNT))
+ return -ENXIO;
+ if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS))
+ return -ENXIO;
mutex_lock(&strings);
if (string == NULL) {
if ((x = snd_sndstat_strings[num][dev]) != NULL) {
diff --git a/sound/core/init.c b/sound/core/init.c
index ac057341613..ef2352c2e45 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -46,17 +46,24 @@ 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
+/* return non-zero if the given index is reserved for the given
* module via slots option
*/
-static int module_slot_mismatch(struct module *module, int idx)
+static int module_slot_match(struct module *module, int idx)
{
+ int match = 1;
#ifdef MODULE
- char *s1, *s2;
+ const char *s1, *s2;
+
if (!module || !module->name || !slots[idx])
return 0;
- s1 = slots[idx];
- s2 = module->name;
+
+ s1 = module->name;
+ s2 = slots[idx];
+ if (*s2 == '!') {
+ match = 0; /* negative match */
+ s2++;
+ }
/* compare module name strings
* hyphens are handled as equivalent with underscore
*/
@@ -68,12 +75,12 @@ static int module_slot_mismatch(struct module *module, int idx)
if (c2 == '-')
c2 = '_';
if (c1 != c2)
- return 1;
+ return !match;
if (!c1)
break;
}
-#endif
- return 0;
+#endif /* MODULE */
+ return match;
}
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
@@ -129,7 +136,7 @@ struct snd_card *snd_card_new(int idx, const char *xid,
struct module *module, int extra_size)
{
struct snd_card *card;
- int err;
+ int err, idx2;
if (extra_size < 0)
extra_size = 0;
@@ -144,35 +151,41 @@ struct snd_card *snd_card_new(int idx, const char *xid,
err = 0;
mutex_lock(&snd_card_mutex);
if (idx < 0) {
- int idx2;
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
/* idx == -1 == 0xffff means: take any free slot */
if (~snd_cards_lock & idx & 1<<idx2) {
- if (module_slot_mismatch(module, idx2))
- continue;
- idx = idx2;
- if (idx >= snd_ecards_limit)
- snd_ecards_limit = idx + 1;
- break;
+ if (module_slot_match(module, idx2)) {
+ idx = idx2;
+ break;
+ }
+ }
+ }
+ if (idx < 0) {
+ for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+ /* idx == -1 == 0xffff means: take any free slot */
+ if (~snd_cards_lock & idx & 1<<idx2) {
+ if (!slots[idx2] || !*slots[idx2]) {
+ idx = idx2;
+ break;
+ }
}
- } else {
- if (idx < snd_ecards_limit) {
- if (snd_cards_lock & (1 << idx))
- err = -EBUSY; /* invalid */
- } else {
- if (idx < SNDRV_CARDS)
- snd_ecards_limit = idx + 1; /* increase the limit */
- else
- err = -ENODEV;
- }
}
- if (idx < 0 || err < 0) {
+ if (idx < 0)
+ err = -ENODEV;
+ else if (idx < snd_ecards_limit) {
+ if (snd_cards_lock & (1 << idx))
+ err = -EBUSY; /* invalid */
+ } else if (idx >= SNDRV_CARDS)
+ err = -ENODEV;
+ if (err < 0) {
mutex_unlock(&snd_card_mutex);
snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
idx, snd_ecards_limit - 1, err);
goto __error;
}
snd_cards_lock |= 1 << idx; /* lock it */
+ if (idx >= snd_ecards_limit)
+ snd_ecards_limit = idx + 1; /* increase the limit */
mutex_unlock(&snd_card_mutex);
card->number = idx;
card->module = module;
@@ -532,10 +545,12 @@ int snd_card_register(struct snd_card *card)
{
int err;
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
#ifndef CONFIG_SYSFS_DEPRECATED
if (!card->card_dev) {
- card->card_dev = device_create(sound_class, card->dev, 0,
+ card->card_dev = device_create(sound_class, card->dev,
+ MKDEV(0, 0), NULL,
"card%i", card->number);
if (IS_ERR(card->card_dev))
card->card_dev = NULL;
diff --git a/sound/core/jack.c b/sound/core/jack.c
new file mode 100644
index 00000000000..8133a2b173a
--- /dev/null
+++ b/sound/core/jack.c
@@ -0,0 +1,163 @@
+/*
+ * Jack abstraction layer
+ *
+ * Copyright 2008 Wolfson Microelectronics
+ *
+ * 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/input.h>
+#include <sound/jack.h>
+#include <sound/core.h>
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+
+ /* If the input device is registered with the input subsystem
+ * then we need to use a different deallocator. */
+ if (jack->registered)
+ input_unregister_device(jack->input_dev);
+ else
+ input_free_device(jack->input_dev);
+
+ kfree(jack);
+
+ return 0;
+}
+
+static int snd_jack_dev_register(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+ struct snd_card *card = device->card;
+ int err;
+
+ snprintf(jack->name, sizeof(jack->name), "%s %s",
+ card->longname, jack->id);
+ jack->input_dev->name = jack->name;
+
+ /* Default to the sound card device. */
+ if (!jack->input_dev->dev.parent)
+ jack->input_dev->dev.parent = card->dev;
+
+ err = input_register_device(jack->input_dev);
+ if (err == 0)
+ jack->registered = 1;
+
+ return err;
+}
+
+/**
+ * snd_jack_new - Create a new jack
+ * @card: the card instance
+ * @id: an identifying string for this jack
+ * @type: a bitmask of enum snd_jack_type values that can be detected by
+ * this jack
+ * @jjack: Used to provide the allocated jack object to the caller.
+ *
+ * Creates a new jack object.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jjack will be initialised.
+ */
+int snd_jack_new(struct snd_card *card, const char *id, int type,
+ struct snd_jack **jjack)
+{
+ struct snd_jack *jack;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_jack_dev_free,
+ .dev_register = snd_jack_dev_register,
+ };
+
+ jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
+ if (jack == NULL)
+ return -ENOMEM;
+
+ jack->id = id;
+
+ jack->input_dev = input_allocate_device();
+ if (jack->input_dev == NULL) {
+ err = -ENOMEM;
+ goto fail_input;
+ }
+
+ jack->input_dev->phys = "ALSA";
+
+ jack->type = type;
+
+ if (type & SND_JACK_HEADPHONE)
+ input_set_capability(jack->input_dev, EV_SW,
+ SW_HEADPHONE_INSERT);
+ if (type & SND_JACK_MICROPHONE)
+ input_set_capability(jack->input_dev, EV_SW,
+ SW_MICROPHONE_INSERT);
+
+ err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
+ if (err < 0)
+ goto fail_input;
+
+ *jjack = jack;
+
+ return 0;
+
+fail_input:
+ input_free_device(jack->input_dev);
+ kfree(jack);
+ return err;
+}
+EXPORT_SYMBOL(snd_jack_new);
+
+/**
+ * snd_jack_set_parent - Set the parent device for a jack
+ *
+ * @jack: The jack to configure
+ * @parent: The device to set as parent for the jack.
+ *
+ * Set the parent for the jack input device in the device tree. This
+ * function is only valid prior to registration of the jack. If no
+ * parent is configured then the parent device will be the sound card.
+ */
+void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
+{
+ WARN_ON(jack->registered);
+
+ jack->input_dev->dev.parent = parent;
+}
+EXPORT_SYMBOL(snd_jack_set_parent);
+
+/**
+ * snd_jack_report - Report the current status of a jack
+ *
+ * @jack: The jack to report status for
+ * @status: The current status of the jack
+ */
+void snd_jack_report(struct snd_jack *jack, int status)
+{
+ if (jack->type & SND_JACK_HEADPHONE)
+ input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
+ status & SND_JACK_HEADPHONE);
+ if (jack->type & SND_JACK_MICROPHONE)
+ input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
+ status & SND_JACK_MICROPHONE);
+
+ input_sync(jack->input_dev);
+}
+EXPORT_SYMBOL(snd_jack_report);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("Jack detection support for ALSA");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 23b7bc02728..1b3534d6768 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -33,9 +33,6 @@
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <sound/memalloc.h>
-#ifdef CONFIG_SBUS
-#include <asm/sbus.h>
-#endif
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
@@ -46,14 +43,6 @@ MODULE_LICENSE("GPL");
/*
*/
-void *snd_malloc_sgbuf_pages(struct device *device,
- size_t size, struct snd_dma_buffer *dmab,
- size_t *res_size);
-int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
-
-/*
- */
-
static DEFINE_MUTEX(list_mutex);
static LIST_HEAD(mem_list_head);
@@ -67,80 +56,6 @@ struct snd_mem_list {
/* id for pre-allocated buffers */
#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
-#ifdef CONFIG_SND_DEBUG
-#define __ASTRING__(x) #x
-#define snd_assert(expr, args...) do {\
- if (!(expr)) {\
- printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
- args;\
- }\
-} while (0)
-#else
-#define snd_assert(expr, args...) /**/
-#endif
-
-/*
- * Hacks
- */
-
-#if defined(__i386__)
-/*
- * A hack to allocate large buffers via dma_alloc_coherent()
- *
- * since dma_alloc_coherent always tries GFP_DMA when the requested
- * pci memory region is below 32bit, it happens quite often that even
- * 2 order of pages cannot be allocated.
- *
- * so in the following, we allocate at first without dma_mask, so that
- * allocation will be done without GFP_DMA. if the area doesn't match
- * with the requested region, then realloate with the original dma_mask
- * again.
- *
- * Really, we want to move this type of thing into dma_alloc_coherent()
- * so dma_mask doesn't have to be messed with.
- */
-
-static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *dma_handle,
- gfp_t flags)
-{
- void *ret;
- u64 dma_mask, coherent_dma_mask;
-
- if (dev == NULL || !dev->dma_mask)
- return dma_alloc_coherent(dev, size, dma_handle, flags);
- dma_mask = *dev->dma_mask;
- coherent_dma_mask = dev->coherent_dma_mask;
- *dev->dma_mask = 0xffffffff; /* do without masking */
- dev->coherent_dma_mask = 0xffffffff; /* do without masking */
- ret = dma_alloc_coherent(dev, size, dma_handle, flags);
- *dev->dma_mask = dma_mask; /* restore */
- dev->coherent_dma_mask = coherent_dma_mask; /* restore */
- if (ret) {
- /* obtained address is out of range? */
- if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) {
- /* reallocate with the proper mask */
- dma_free_coherent(dev, size, ret, *dma_handle);
- ret = dma_alloc_coherent(dev, size, dma_handle, flags);
- }
- } else {
- /* wish to success now with the proper mask... */
- if (dma_mask != 0xffffffffUL) {
- /* allocation with GFP_ATOMIC to avoid the long stall */
- flags &= ~GFP_KERNEL;
- flags |= GFP_ATOMIC;
- ret = dma_alloc_coherent(dev, size, dma_handle, flags);
- }
- }
- return ret;
-}
-
-/* redefine dma_alloc_coherent for some architectures */
-#undef dma_alloc_coherent
-#define dma_alloc_coherent snd_dma_hack_alloc_coherent
-
-#endif /* arch */
-
/*
*
* Generic memory allocators
@@ -173,8 +88,10 @@ void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
int pg;
void *res;
- snd_assert(size > 0, return NULL);
- snd_assert(gfp_flags != 0, return NULL);
+ if (WARN_ON(!size))
+ return NULL;
+ if (WARN_ON(!gfp_flags))
+ return NULL;
gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
pg = get_order(size);
if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL)
@@ -214,8 +131,8 @@ static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *d
void *res;
gfp_t gfp_flags;
- snd_assert(size > 0, return NULL);
- snd_assert(dma != NULL, return NULL);
+ if (WARN_ON(!dma))
+ return NULL;
pg = get_order(size);
gfp_flags = GFP_KERNEL
| __GFP_COMP /* compound page lets parts be mapped */
@@ -242,39 +159,6 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
}
#endif /* CONFIG_HAS_DMA */
-#ifdef CONFIG_SBUS
-
-static void *snd_malloc_sbus_pages(struct device *dev, size_t size,
- dma_addr_t *dma_addr)
-{
- struct sbus_dev *sdev = (struct sbus_dev *)dev;
- int pg;
- void *res;
-
- snd_assert(size > 0, return NULL);
- snd_assert(dma_addr != NULL, return NULL);
- pg = get_order(size);
- res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
- if (res != NULL)
- inc_snd_pages(pg);
- return res;
-}
-
-static void snd_free_sbus_pages(struct device *dev, size_t size,
- void *ptr, dma_addr_t dma_addr)
-{
- struct sbus_dev *sdev = (struct sbus_dev *)dev;
- int pg;
-
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dec_snd_pages(pg);
- sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
-}
-
-#endif /* CONFIG_SBUS */
-
/*
*
* ALSA generic memory management
@@ -298,8 +182,10 @@ static void snd_free_sbus_pages(struct device *dev, size_t size,
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
{
- snd_assert(size > 0, return -ENXIO);
- snd_assert(dmab != NULL, return -ENXIO);
+ if (WARN_ON(!size))
+ return -ENXIO;
+ if (WARN_ON(!dmab))
+ return -ENXIO;
dmab->dev.type = type;
dmab->dev.dev = device;
@@ -309,11 +195,6 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
dmab->area = snd_malloc_pages(size, (unsigned long)device);
dmab->addr = 0;
break;
-#ifdef CONFIG_SBUS
- case SNDRV_DMA_TYPE_SBUS:
- dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
- break;
-#endif
#ifdef CONFIG_HAS_DMA
case SNDRV_DMA_TYPE_DEV:
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
@@ -354,15 +235,17 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
{
int err;
- snd_assert(size > 0, return -ENXIO);
- snd_assert(dmab != NULL, return -ENXIO);
-
while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
+ size_t aligned_size;
if (err != -ENOMEM)
return err;
- size >>= 1;
if (size <= PAGE_SIZE)
return -ENOMEM;
+ aligned_size = PAGE_SIZE << get_order(size);
+ if (size != aligned_size)
+ size = aligned_size;
+ else
+ size >>= 1;
}
if (! dmab->area)
return -ENOMEM;
@@ -382,11 +265,6 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
case SNDRV_DMA_TYPE_CONTINUOUS:
snd_free_pages(dmab->area, dmab->bytes);
break;
-#ifdef CONFIG_SBUS
- case SNDRV_DMA_TYPE_SBUS:
- snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
- break;
-#endif
#ifdef CONFIG_HAS_DMA
case SNDRV_DMA_TYPE_DEV:
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
@@ -415,7 +293,8 @@ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
{
struct snd_mem_list *mem;
- snd_assert(dmab, return 0);
+ if (WARN_ON(!dmab))
+ return 0;
mutex_lock(&list_mutex);
list_for_each_entry(mem, &mem_list_head, list) {
@@ -449,7 +328,8 @@ int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
{
struct snd_mem_list *mem;
- snd_assert(dmab, return -EINVAL);
+ if (WARN_ON(!dmab))
+ return -EINVAL;
mem = kmalloc(sizeof(*mem), GFP_KERNEL);
if (! mem)
return -ENOMEM;
@@ -493,7 +373,7 @@ static int snd_mem_proc_read(struct seq_file *seq, void *offset)
long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
struct snd_mem_list *mem;
int devno;
- static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
+ static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" };
mutex_lock(&list_mutex);
seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n",
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
index 9ded30d0e97..05b58d4fc2b 100644
--- a/sound/core/oss/copy.c
+++ b/sound/core/oss/copy.c
@@ -32,17 +32,18 @@ static snd_pcm_sframes_t copy_transfer(struct snd_pcm_plugin *plugin,
unsigned int channel;
unsigned int nchannels;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
nchannels = plugin->src_format.channels;
for (channel = 0; channel < nchannels; channel++) {
- snd_assert(src_channels->area.first % 8 == 0 &&
- src_channels->area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels->area.first % 8 == 0 &&
- dst_channels->area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels->area.first % 8 ||
+ src_channels->area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels->area.first % 8 ||
+ dst_channels->area.step % 8))
+ return -ENXIO;
if (!src_channels->enabled) {
if (dst_channels->wanted)
snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
@@ -66,15 +67,20 @@ int snd_pcm_plugin_build_copy(struct snd_pcm_substream *plug,
struct snd_pcm_plugin *plugin;
int width;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->format == dst_format->format, return -ENXIO);
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ if (snd_BUG_ON(src_format->format != dst_format->format))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
width = snd_pcm_format_physical_width(src_format->format);
- snd_assert(width > 0, return -ENXIO);
+ if (snd_BUG_ON(width <= 0))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
0, &plugin);
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
index f874f6ca365..6faa1d71920 100644
--- a/sound/core/oss/io.c
+++ b/sound/core/oss/io.c
@@ -39,14 +39,17 @@ static snd_pcm_sframes_t io_playback_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- snd_assert(plugin != NULL, return -ENXIO);
- snd_assert(src_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
+ if (snd_BUG_ON(!src_channels))
+ return -ENXIO;
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
return pcm_write(plugin->plug, src_channels->area.addr, frames);
} else {
int channel, channels = plugin->dst_format.channels;
void **bufs = (void**)plugin->extra_data;
- snd_assert(bufs != NULL, return -ENXIO);
+ if (snd_BUG_ON(!bufs))
+ return -ENXIO;
for (channel = 0; channel < channels; channel++) {
if (src_channels[channel].enabled)
bufs[channel] = src_channels[channel].area.addr;
@@ -62,14 +65,17 @@ static snd_pcm_sframes_t io_capture_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- snd_assert(plugin != NULL, return -ENXIO);
- snd_assert(dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
+ if (snd_BUG_ON(!dst_channels))
+ return -ENXIO;
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
return pcm_read(plugin->plug, dst_channels->area.addr, frames);
} else {
int channel, channels = plugin->dst_format.channels;
void **bufs = (void**)plugin->extra_data;
- snd_assert(bufs != NULL, return -ENXIO);
+ if (snd_BUG_ON(!bufs))
+ return -ENXIO;
for (channel = 0; channel < channels; channel++) {
if (dst_channels[channel].enabled)
bufs[channel] = dst_channels[channel].area.addr;
@@ -107,9 +113,11 @@ int snd_pcm_plugin_build_io(struct snd_pcm_substream *plug,
struct snd_pcm_plugin_format format;
struct snd_pcm_plugin *plugin;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(plug != NULL && params != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug || !params))
+ return -ENXIO;
format.format = params_format(params);
format.rate = params_rate(params);
format.channels = params_channels(params);
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index da3dbd41669..4c1d1682719 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -92,7 +92,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
{
struct linear_priv *data;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
data = (struct linear_priv *)plugin->extra_data;
if (frames == 0)
return 0;
@@ -100,12 +101,12 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
{
unsigned int channel;
for (channel = 0; channel < plugin->src_format.channels; channel++) {
- snd_assert(src_channels[channel].area.first % 8 == 0 &&
- src_channels[channel].area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels[channel].area.first % 8 == 0 &&
- dst_channels[channel].area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+ src_channels[channel].area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+ dst_channels[channel].area.step % 8))
+ return -ENXIO;
}
}
#endif
@@ -154,13 +155,17 @@ int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
struct linear_priv *data;
struct snd_pcm_plugin *plugin;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
- snd_assert(snd_pcm_format_linear(src_format->format) &&
- snd_pcm_format_linear(dst_format->format), return -ENXIO);
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
+ if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||
+ !snd_pcm_format_linear(dst_format->format)))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "linear format conversion",
src_format, dst_format,
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 581aa2c60e6..4690b8b5681 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -257,8 +257,10 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
result = pslot->get_volume(fmixer, pslot, &left, &right);
if (!pslot->stereo)
right = left;
- snd_assert(left >= 0 && left <= 100, return -EIO);
- snd_assert(right >= 0 && right <= 100, return -EIO);
+ if (snd_BUG_ON(left < 0 || left > 100))
+ return -EIO;
+ if (snd_BUG_ON(right < 0 || right > 100))
+ return -EIO;
if (result >= 0) {
pslot->volume[0] = left;
pslot->volume[1] = right;
@@ -298,7 +300,8 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int
int __user *p = argp;
int tmp;
- snd_assert(fmixer != NULL, return -ENXIO);
+ if (snd_BUG_ON(!fmixer))
+ return -ENXIO;
if (((cmd >> 8) & 0xff) == 'M') {
switch (cmd) {
case SOUND_MIXER_INFO:
@@ -368,7 +371,8 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l
{
struct snd_mixer_oss_file fmixer;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
if (card->mixer_oss == NULL)
return -ENXIO;
memset(&fmixer, 0, sizeof(fmixer));
@@ -1284,9 +1288,11 @@ static int snd_mixer_oss_free1(void *private)
struct snd_card *card;
int idx;
- snd_assert(mixer != NULL, return -ENXIO);
+ if (!mixer)
+ return 0;
card = mixer->card;
- snd_assert(mixer == card->mixer_oss, return -ENXIO);
+ if (snd_BUG_ON(mixer != card->mixer_oss))
+ return -ENXIO;
card->mixer_oss = NULL;
for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
struct snd_mixer_oss_slot *chn = &mixer->slots[idx];
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index 77f96194a0e..f7649d4d950 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -252,19 +252,20 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
{
struct mulaw_priv *data;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
{
unsigned int channel;
for (channel = 0; channel < plugin->src_format.channels; channel++) {
- snd_assert(src_channels[channel].area.first % 8 == 0 &&
- src_channels[channel].area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels[channel].area.first % 8 == 0 &&
- dst_channels[channel].area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+ src_channels[channel].area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+ dst_channels[channel].area.step % 8))
+ return -ENXIO;
}
}
#endif
@@ -305,11 +306,14 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
struct snd_pcm_plugin_format *format;
mulaw_f func;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
format = src_format;
@@ -323,7 +327,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
snd_BUG();
return -EINVAL;
}
- snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
+ if (snd_BUG_ON(!snd_pcm_format_linear(format->format)))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
src_format, dst_format,
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 4c601b192dd..1af62b8b86c 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -452,7 +452,8 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
} else {
*params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
- snd_assert(max >= 0, return -EINVAL);
+ if (max < 0)
+ return max;
last = 1;
}
_end:
@@ -461,7 +462,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
v = snd_pcm_hw_param_first(pcm, params, var, dir);
- snd_assert(v >= 0, return -EINVAL);
+ snd_BUG_ON(v < 0);
return v;
}
@@ -778,7 +779,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
while (oss_period_size * oss_periods > oss_buffer_size)
oss_period_size /= 2;
- snd_assert(oss_period_size >= 16, return -EINVAL);
+ if (oss_period_size < 16)
+ return -EINVAL;
runtime->oss.period_bytes = oss_period_size;
runtime->oss.period_frames = 1;
runtime->oss.periods = oss_periods;
@@ -895,7 +897,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
}
}
err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
- snd_assert(err >= 0, goto failure);
+ if (err < 0)
+ goto failure;
if (direct) {
memcpy(params, sparams, sizeof(*params));
@@ -958,11 +961,13 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
- snd_assert(err >= 0, goto failure);
+ if (err < 0)
+ goto failure;
err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
runtime->oss.periods, NULL);
- snd_assert(err >= 0, goto failure);
+ if (err < 0)
+ goto failure;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
@@ -1006,7 +1011,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
runtime->oss.periods = params_periods(sparams);
oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
- snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure);
+ if (oss_period_size < 0) {
+ err = -EINVAL;
+ goto failure;
+ }
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
if (runtime->oss.plugin_first) {
err = snd_pcm_plug_alloc(substream, oss_period_size);
@@ -1017,7 +1025,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
oss_period_size *= oss_frame_size;
oss_buffer_size = oss_period_size * runtime->oss.periods;
- snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure);
+ if (oss_buffer_size < 0) {
+ err = -EINVAL;
+ goto failure;
+ }
runtime->oss.period_bytes = oss_period_size;
runtime->oss.buffer_bytes = oss_buffer_size;
@@ -1069,7 +1080,8 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
return err;
}
}
- snd_assert(asubstream != NULL, return -EIO);
+ if (!asubstream)
+ return -EIO;
if (r_substream)
*r_substream = asubstream;
return 0;
@@ -1764,7 +1776,8 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
err = snd_pcm_hw_refine(substream, params);
format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
kfree(params);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
for (fmt = 0; fmt < 32; ++fmt) {
if (snd_mask_test(&format_mask, fmt)) {
int f = snd_pcm_oss_format_to(fmt);
@@ -2250,7 +2263,8 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
{
int cidx;
- snd_assert(pcm_oss_file != NULL, return -ENXIO);
+ if (!pcm_oss_file)
+ return 0;
for (cidx = 0; cidx < 2; ++cidx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];
if (substream)
@@ -2271,8 +2285,8 @@ static int snd_pcm_oss_open_file(struct file *file,
struct snd_pcm_substream *substream;
unsigned int f_mode = file->f_mode;
- snd_assert(rpcm_oss_file != NULL, return -EINVAL);
- *rpcm_oss_file = NULL;
+ if (rpcm_oss_file)
+ *rpcm_oss_file = NULL;
pcm_oss_file = kzalloc(sizeof(*pcm_oss_file), GFP_KERNEL);
if (pcm_oss_file == NULL)
@@ -2312,7 +2326,8 @@ static int snd_pcm_oss_open_file(struct file *file,
}
file->private_data = pcm_oss_file;
- *rpcm_oss_file = pcm_oss_file;
+ if (rpcm_oss_file)
+ *rpcm_oss_file = pcm_oss_file;
return 0;
}
@@ -2321,7 +2336,8 @@ static int snd_task_name(struct task_struct *task, char *name, size_t size)
{
unsigned int idx;
- snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
+ if (snd_BUG_ON(!task || !name || size < 2))
+ return -EINVAL;
for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
name[idx] = task->comm[idx];
name[idx] = '\0';
@@ -2415,7 +2431,8 @@ static int snd_pcm_oss_release(struct inode *inode, struct file *file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream == NULL)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
- snd_assert(substream != NULL, return -ENXIO);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
pcm = substream->pcm;
if (!pcm->card->shutdown)
snd_pcm_oss_sync(pcm_oss_file);
@@ -2448,7 +2465,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
if (substream != NULL)
break;
}
- snd_assert(substream != NULL, return -ENXIO);
+ if (snd_BUG_ON(idx >= 2))
+ return -ENXIO;
return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
}
#endif
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index bec94138205..6751daa3bb5 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -62,7 +62,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
return width;
size = frames * format->channels * width;
- snd_assert((size % 8) == 0, return -ENXIO);
+ if (snd_BUG_ON(size % 8))
+ return -ENXIO;
size /= 8;
if (plugin->buf_frames < frames) {
vfree(plugin->buf);
@@ -84,7 +85,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
c->area.step = format->channels * width;
}
} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
- snd_assert((size % format->channels) == 0,);
+ if (snd_BUG_ON(size % format->channels))
+ return -EINVAL;
size /= format->channels;
for (channel = 0; channel < format->channels; channel++, c++) {
c->frames = frames;
@@ -102,13 +104,15 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
{
int err;
- snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
+ if (snd_BUG_ON(!snd_pcm_plug_first(plug)))
+ return -ENXIO;
if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);
while (plugin->next) {
if (plugin->dst_frames)
frames = plugin->dst_frames(plugin, frames);
- snd_assert(frames > 0, return -ENXIO);
+ if (snd_BUG_ON(frames <= 0))
+ return -ENXIO;
plugin = plugin->next;
err = snd_pcm_plugin_alloc(plugin, frames);
if (err < 0)
@@ -119,7 +123,8 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->prev) {
if (plugin->src_frames)
frames = plugin->src_frames(plugin, frames);
- snd_assert(frames > 0, return -ENXIO);
+ if (snd_BUG_ON(frames <= 0))
+ return -ENXIO;
plugin = plugin->prev;
err = snd_pcm_plugin_alloc(plugin, frames);
if (err < 0)
@@ -148,8 +153,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
struct snd_pcm_plugin *plugin;
unsigned int channels;
- snd_assert(plug != NULL, return -ENXIO);
- snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
+ if (snd_BUG_ON(!src_format || !dst_format))
+ return -ENXIO;
plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);
if (plugin == NULL)
return -ENOMEM;
@@ -159,10 +166,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
plugin->src_format = *src_format;
plugin->src_width = snd_pcm_format_physical_width(src_format->format);
- snd_assert(plugin->src_width > 0, );
+ snd_BUG_ON(plugin->src_width <= 0);
plugin->dst_format = *dst_format;
plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
- snd_assert(plugin->dst_width > 0, );
+ snd_BUG_ON(plugin->dst_width <= 0);
if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
channels = src_format->channels;
else
@@ -194,7 +201,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_p
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
int stream = snd_pcm_plug_stream(plug);
- snd_assert(plug != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
if (drv_frames == 0)
return 0;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -224,7 +232,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
snd_pcm_sframes_t frames;
int stream = snd_pcm_plug_stream(plug);
- snd_assert(plug != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
if (clt_frames == 0)
return 0;
frames = clt_frames;
@@ -540,7 +549,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
int width, nchannels, channel;
int stream = snd_pcm_plug_stream(plug);
- snd_assert(buf != NULL, return -ENXIO);
+ if (snd_BUG_ON(!buf))
+ return -ENXIO;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
plugin = snd_pcm_plug_first(plug);
format = &plugin->src_format;
@@ -553,7 +563,9 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
return width;
nchannels = format->channels;
- snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
+ if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
+ format->channels > 1))
+ return -ENXIO;
for (channel = 0; channel < nchannels; channel++, v++) {
v->frames = count;
v->enabled = 1;
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 14dfb3175d8..a466443c4a2 100644
--- a/sound/core/oss/rate.c
+++ b/sound/core/oss/rate.c
@@ -185,7 +185,8 @@ static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_
struct rate_priv *data;
snd_pcm_sframes_t res;
- snd_assert(plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
if (frames == 0)
return 0;
data = (struct rate_priv *)plugin->extra_data;
@@ -217,7 +218,8 @@ static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_
struct rate_priv *data;
snd_pcm_sframes_t res;
- snd_assert(plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
if (frames == 0)
return 0;
data = (struct rate_priv *)plugin->extra_data;
@@ -252,19 +254,20 @@ static snd_pcm_sframes_t rate_transfer(struct snd_pcm_plugin *plugin,
snd_pcm_uframes_t dst_frames;
struct rate_priv *data;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
{
unsigned int channel;
for (channel = 0; channel < plugin->src_format.channels; channel++) {
- snd_assert(src_channels[channel].area.first % 8 == 0 &&
- src_channels[channel].area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels[channel].area.first % 8 == 0 &&
- dst_channels[channel].area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+ src_channels[channel].area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+ dst_channels[channel].area.step % 8))
+ return -ENXIO;
}
}
#endif
@@ -281,7 +284,8 @@ static int rate_action(struct snd_pcm_plugin *plugin,
enum snd_pcm_plugin_action action,
unsigned long udata)
{
- snd_assert(plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
switch (action) {
case INIT:
case PREPARE:
@@ -302,14 +306,20 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
struct rate_priv *data;
struct snd_pcm_plugin *plugin;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
- snd_assert(src_format->channels > 0, return -ENXIO);
- snd_assert(src_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO);
- snd_assert(dst_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO);
- snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels <= 0))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->format != SNDRV_PCM_FORMAT_S16))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_format->format != SNDRV_PCM_FORMAT_S16))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->rate == dst_format->rate))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "rate conversion",
src_format, dst_format,
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index da7ab7a3e82..0dcc2870d53 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -54,7 +54,8 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dvp;
int format;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
@@ -90,10 +91,13 @@ int snd_pcm_plugin_build_route(struct snd_pcm_substream *plug,
struct snd_pcm_plugin *plugin;
int err;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->format == dst_format->format, return -ENXIO);
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->format != dst_format->format))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "route conversion",
src_format, dst_format, 0, &plugin);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 9dd9bc73fe1..192a433a240 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -42,7 +42,7 @@ static int snd_pcm_dev_free(struct snd_device *device);
static int snd_pcm_dev_register(struct snd_device *device);
static int snd_pcm_dev_disconnect(struct snd_device *device);
-static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
+static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
{
struct snd_pcm *pcm;
@@ -53,6 +53,37 @@ static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
return NULL;
}
+static int snd_pcm_next(struct snd_card *card, int device)
+{
+ struct snd_pcm *pcm;
+
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->card == card && pcm->device > device)
+ return pcm->device;
+ else if (pcm->card->number > card->number)
+ return -1;
+ }
+ return -1;
+}
+
+static int snd_pcm_add(struct snd_pcm *newpcm)
+{
+ struct snd_pcm *pcm;
+
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->card == newpcm->card && pcm->device == newpcm->device)
+ return -EBUSY;
+ if (pcm->card->number > newpcm->card->number ||
+ (pcm->card == newpcm->card &&
+ pcm->device > newpcm->device)) {
+ list_add(&newpcm->list, pcm->list.prev);
+ return 0;
+ }
+ }
+ list_add_tail(&newpcm->list, &snd_pcm_devices);
+ return 0;
+}
+
static int snd_pcm_control_ioctl(struct snd_card *card,
struct snd_ctl_file *control,
unsigned int cmd, unsigned long arg)
@@ -65,14 +96,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
mutex_lock(&register_mutex);
- device = device < 0 ? 0 : device + 1;
- while (device < SNDRV_PCM_DEVICES) {
- if (snd_pcm_search(card, device))
- break;
- device++;
- }
- if (device == SNDRV_PCM_DEVICES)
- device = -1;
+ device = snd_pcm_next(card, device);
mutex_unlock(&register_mutex);
if (put_user(device, (int __user *)arg))
return -EFAULT;
@@ -98,7 +122,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(subdevice, &info->subdevice))
return -EFAULT;
mutex_lock(&register_mutex);
- pcm = snd_pcm_search(card, device);
+ pcm = snd_pcm_get(card, device);
if (pcm == NULL) {
err = -ENXIO;
goto _error;
@@ -232,7 +256,6 @@ static char *snd_pcm_tstamp_mode_names[] = {
static const char *snd_pcm_stream_name(int stream)
{
- snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL);
return snd_pcm_stream_names[stream];
}
@@ -248,7 +271,6 @@ static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
static const char *snd_pcm_tstamp_mode_name(int mode)
{
- snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL);
return snd_pcm_tstamp_mode_names[mode];
}
@@ -682,9 +704,10 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
.dev_disconnect = snd_pcm_dev_disconnect,
};
- snd_assert(rpcm != NULL, return -EINVAL);
- *rpcm = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rpcm)
+ *rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
snd_printk(KERN_ERR "Cannot allocate PCM\n");
@@ -708,7 +731,8 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
snd_pcm_free(pcm);
return err;
}
- *rpcm = pcm;
+ if (rpcm)
+ *rpcm = pcm;
return 0;
}
@@ -742,7 +766,8 @@ static int snd_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_notify *notify;
- snd_assert(pcm != NULL, return -ENXIO);
+ if (!pcm)
+ return 0;
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_unregister(pcm);
}
@@ -773,15 +798,15 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
int prefer_subdevice = -1;
size_t size;
- snd_assert(rsubstream != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pcm || !rsubstream))
+ return -ENXIO;
*rsubstream = NULL;
- snd_assert(pcm != NULL, return -ENXIO);
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
return -ENODEV;
card = pcm->card;
- down_read(&card->controls_rwsem);
+ read_lock(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == current->pid) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
@@ -789,7 +814,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
break;
}
}
- up_read(&card->controls_rwsem);
+ read_unlock(&card->ctl_files_rwlock);
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
@@ -883,8 +908,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
+ if (PCM_RUNTIME_CHECK(substream))
+ return;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return);
if (runtime->private_free != NULL)
runtime->private_free(runtime);
snd_free_pages((void*)runtime->status,
@@ -929,13 +955,14 @@ static int snd_pcm_dev_register(struct snd_device *device)
struct snd_pcm *pcm = device->device_data;
struct device *dev;
- snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+ if (snd_BUG_ON(!pcm || !device))
+ return -ENXIO;
mutex_lock(&register_mutex);
- if (snd_pcm_search(pcm->card, pcm->device)) {
+ err = snd_pcm_add(pcm);
+ if (err) {
mutex_unlock(&register_mutex);
- return -EBUSY;
+ return err;
}
- list_add_tail(&pcm->list, &snd_pcm_devices);
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
@@ -1019,10 +1046,11 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct snd_pcm *pcm;
- snd_assert(notify != NULL &&
- notify->n_register != NULL &&
- notify->n_unregister != NULL &&
- notify->n_disconnect, return -EINVAL);
+ if (snd_BUG_ON(!notify ||
+ !notify->n_register ||
+ !notify->n_unregister ||
+ !notify->n_disconnect))
+ return -EINVAL;
mutex_lock(&register_mutex);
if (nfree) {
list_del(&notify->list);
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 49aa693fba8..36d7a599823 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -397,7 +397,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
snd_pcm_uframes_t boundary;
int err;
- snd_assert(runtime, return -EINVAL);
+ if (snd_BUG_ON(!runtime))
+ return -EINVAL;
if (get_user(sflags, &src->flags) ||
get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 1533f0379e9..6ea5cfb8399 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -85,7 +85,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
}
frames = runtime->buffer_size - runtime->silence_filled;
}
- snd_assert(frames <= runtime->buffer_size, return);
+ if (snd_BUG_ON(frames > runtime->buffer_size))
+ return;
if (frames == 0)
return;
ofs = runtime->silence_start % runtime->buffer_size;
@@ -96,7 +97,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
if (substream->ops->silence) {
int err;
err = substream->ops->silence(substream, -1, ofs, transfer);
- snd_assert(err >= 0, );
+ snd_BUG_ON(err < 0);
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
@@ -108,7 +109,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
for (c = 0; c < channels; ++c) {
int err;
err = substream->ops->silence(substream, c, ofs, transfer);
- snd_assert(err >= 0, );
+ snd_BUG_ON(err < 0);
}
} else {
size_t dma_csize = runtime->dma_bytes / channels;
@@ -354,7 +355,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
{
u_int64_t n = (u_int64_t) a * b;
if (c == 0) {
- snd_assert(n > 0, );
+ snd_BUG_ON(!n);
*r = 0;
return UINT_MAX;
}
@@ -380,7 +381,8 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
int changed = 0;
- snd_assert(!snd_interval_empty(i), return -EINVAL);
+ if (snd_BUG_ON(snd_interval_empty(i)))
+ return -EINVAL;
if (i->min < v->min) {
i->min = v->min;
i->openmin = v->openmin;
@@ -423,7 +425,8 @@ EXPORT_SYMBOL(snd_interval_refine);
static int snd_interval_refine_first(struct snd_interval *i)
{
- snd_assert(!snd_interval_empty(i), return -EINVAL);
+ if (snd_BUG_ON(snd_interval_empty(i)))
+ return -EINVAL;
if (snd_interval_single(i))
return 0;
i->max = i->min;
@@ -435,7 +438,8 @@ static int snd_interval_refine_first(struct snd_interval *i)
static int snd_interval_refine_last(struct snd_interval *i)
{
- snd_assert(!snd_interval_empty(i), return -EINVAL);
+ if (snd_BUG_ON(snd_interval_empty(i)))
+ return -EINVAL;
if (snd_interval_single(i))
return 0;
i->min = i->max;
@@ -889,7 +893,8 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
c->private = private;
k = 0;
while (1) {
- snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL);
+ if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps)))
+ return -EINVAL;
c->deps[k++] = dep;
if (dep < 0)
break;
@@ -1285,7 +1290,8 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- snd_assert(err >= 0, return err);
+ if (snd_BUG_ON(err < 0))
+ return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
@@ -1330,7 +1336,8 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- snd_assert(err >= 0, return err);
+ if (snd_BUG_ON(err < 0))
+ return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
@@ -1368,7 +1375,8 @@ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
else
err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
- snd_assert(err >= 0, return err);
+ if (snd_BUG_ON(err < 0))
+ return err;
}
return 0;
}
@@ -1466,9 +1474,9 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
unsigned long flags;
- snd_assert(substream != NULL, return);
+ if (PCM_RUNTIME_CHECK(substream))
+ return;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return);
if (runtime->transfer_ack_begin)
runtime->transfer_ack_begin(substream);
@@ -1567,7 +1575,6 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- snd_assert(runtime->dma_area, return -EFAULT);
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
@@ -1629,7 +1636,10 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
if (frames > cont)
frames = cont;
- snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+ if (snd_BUG_ON(!frames)) {
+ snd_pcm_stream_unlock_irq(substream);
+ return -EINVAL;
+ }
appl_ptr = runtime->control->appl_ptr;
appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
@@ -1669,18 +1679,30 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
-snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
+/* sanity-check for read/write methods */
+static int pcm_sanity_check(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- int nonblock;
-
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+ if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
+ return -EINVAL;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
+{
+ struct snd_pcm_runtime *runtime;
+ int nonblock;
+ int err;
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
+ runtime = substream->runtime;
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
@@ -1703,7 +1725,8 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
int channels = runtime->channels;
int c;
if (substream->ops->copy) {
- snd_assert(substream->ops->silence != NULL, return -EINVAL);
+ if (snd_BUG_ON(!substream->ops->silence))
+ return -EINVAL;
for (c = 0; c < channels; ++c, ++bufs) {
if (*bufs == NULL) {
if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
@@ -1717,7 +1740,6 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
} else {
/* default transfer behaviour */
size_t dma_csize = runtime->dma_bytes / channels;
- snd_assert(runtime->dma_area, return -EFAULT);
for (c = 0; c < channels; ++c, ++bufs) {
char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
if (*bufs == NULL) {
@@ -1738,14 +1760,12 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime;
int nonblock;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
-
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
@@ -1769,7 +1789,6 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- snd_assert(runtime->dma_area, return -EFAULT);
if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
@@ -1841,7 +1860,10 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
if (frames > cont)
frames = cont;
- snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+ if (snd_BUG_ON(!frames)) {
+ snd_pcm_stream_unlock_irq(substream);
+ return -EINVAL;
+ }
appl_ptr = runtime->control->appl_ptr;
appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
@@ -1879,14 +1901,12 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u
{
struct snd_pcm_runtime *runtime;
int nonblock;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
-
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
return -EINVAL;
@@ -1916,7 +1936,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
}
} else {
snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
- snd_assert(runtime->dma_area, return -EFAULT);
for (c = 0; c < channels; ++c, ++bufs) {
char *hwbuf;
char __user *buf;
@@ -1938,11 +1957,12 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime;
int nonblock;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index ff07b4a9992..a6d42808828 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -50,8 +50,6 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz
struct snd_dma_buffer *dmab = &substream->dma_buffer;
int err;
- snd_assert(size > 0, return -EINVAL);
-
/* already reserved? */
if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
if (dmab->bytes >= size)
@@ -326,6 +324,32 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
+/*
+ * compute the max chunk size with continuous pages on sg-buffer
+ */
+unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
+ unsigned int ofs, unsigned int size)
+{
+ struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
+ unsigned int start, end, pg;
+
+ start = ofs >> PAGE_SHIFT;
+ end = (ofs + size - 1) >> PAGE_SHIFT;
+ /* check page continuity */
+ pg = sg->table[start].addr >> PAGE_SHIFT;
+ for (;;) {
+ start++;
+ if (start > end)
+ break;
+ pg++;
+ if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
+ return (start << PAGE_SHIFT) - ofs;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
+
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
* @substream: the substream to allocate the DMA buffer to
@@ -342,10 +366,12 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
struct snd_pcm_runtime *runtime;
struct snd_dma_buffer *dmab = NULL;
- snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL);
- snd_assert(substream != NULL, return -EINVAL);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -EINVAL;
+ if (snd_BUG_ON(substream->dma_buffer.dev.type ==
+ SNDRV_DMA_TYPE_UNKNOWN))
+ return -EINVAL;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EINVAL);
if (runtime->dma_buffer_p) {
/* perphaps, we might free the large DMA memory region
@@ -391,9 +417,9 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- snd_assert(substream != NULL, return -EINVAL);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -EINVAL;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EINVAL);
if (runtime->dma_area == NULL)
return 0;
if (runtime->dma_buffer_p != &substream->dma_buffer) {
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 61f5d425b63..e61e12506de 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -22,6 +22,7 @@
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/time.h>
#include <linux/pm_qos_params.h>
#include <linux/uio.h>
@@ -94,7 +95,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
struct snd_pcm *pcm = substream->pcm;
struct snd_pcm_str *pstr = substream->pstr;
- snd_assert(substream != NULL, return -ENXIO);
memset(info, 0, sizeof(*info));
info->card = pcm->card->number;
info->device = pcm->device;
@@ -369,9 +369,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int bits;
snd_pcm_uframes_t frames;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
case SNDRV_PCM_STATE_OPEN:
@@ -489,9 +489,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
int result = 0;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
case SNDRV_PCM_STATE_SETUP:
@@ -517,9 +517,9 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
snd_pcm_stream_lock_irq(substream);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_pcm_stream_unlock_irq(substream);
@@ -621,11 +621,8 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
struct snd_pcm_status __user * _status)
{
struct snd_pcm_status status;
- struct snd_pcm_runtime *runtime;
int res;
- snd_assert(substream != NULL, return -ENXIO);
- runtime = substream->runtime;
memset(&status, 0, sizeof(status));
res = snd_pcm_status(substream, &status);
if (res < 0)
@@ -641,7 +638,6 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime;
unsigned int channel;
- snd_assert(substream != NULL, return -ENXIO);
channel = info->channel;
runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
@@ -1249,7 +1245,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
if (err < 0)
return err;
- // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );
runtime->hw_ptr_base = 0;
runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
runtime->status->hw_ptr % runtime->period_size;
@@ -1420,7 +1415,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
int i, num_drecs;
struct drain_rec *drec, drec_tmp, *d;
- snd_assert(substream != NULL, return -ENXIO);
card = substream->pcm->card;
runtime = substream->runtime;
@@ -1540,21 +1534,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
struct snd_card *card;
int result = 0;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
card = substream->pcm->card;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
return -EBADFD;
- snd_power_lock(card);
- if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result < 0)
- goto _unlock;
- }
-
snd_pcm_stream_lock_irq(substream);
/* resume pause */
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@@ -1563,8 +1552,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
snd_pcm_stream_unlock_irq(substream);
- _unlock:
- snd_power_unlock(card);
+
return result;
}
@@ -1940,33 +1928,41 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
}
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
hw->channels_min, hw->channels_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
hw->rate_min, hw->rate_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
hw->period_bytes_min, hw->period_bytes_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
hw->periods_min, hw->periods_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
hw->period_bytes_min, hw->buffer_bytes_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
snd_pcm_hw_rule_buffer_bytes_max, substream,
@@ -1977,7 +1973,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
/* FIXME: remove */
if (runtime->dma_bytes) {
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return -EINVAL;
}
if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
@@ -2073,8 +2070,8 @@ static int snd_pcm_open_file(struct file *file,
struct snd_pcm_str *str;
int err;
- snd_assert(rpcm_file != NULL, return -EINVAL);
- *rpcm_file = NULL;
+ if (rpcm_file)
+ *rpcm_file = NULL;
err = snd_pcm_open_substream(pcm, stream, file, &substream);
if (err < 0)
@@ -2092,7 +2089,8 @@ static int snd_pcm_open_file(struct file *file,
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file;
- *rpcm_file = pcm_file;
+ if (rpcm_file)
+ *rpcm_file = pcm_file;
return 0;
}
@@ -2176,7 +2174,8 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
pcm = substream->pcm;
fasync_helper(-1, file, 0, &substream->runtime->fasync);
mutex_lock(&pcm->open_mutex);
@@ -2499,8 +2498,6 @@ static int snd_pcm_common_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
- snd_assert(substream != NULL, return -ENXIO);
-
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
@@ -2569,8 +2566,10 @@ static int snd_pcm_playback_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
- snd_assert(substream != NULL, return -ENXIO);
- snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
+ if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
+ return -EINVAL;
switch (cmd) {
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
{
@@ -2649,8 +2648,10 @@ static int snd_pcm_capture_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
- snd_assert(substream != NULL, return -ENXIO);
- snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
+ if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE))
+ return -EINVAL;
switch (cmd) {
case SNDRV_PCM_IOCTL_READI_FRAMES:
{
@@ -2789,7 +2790,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
@@ -2812,21 +2814,17 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, result = -ENXIO; goto end);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- result = -EBADFD;
- goto end;
- }
- if (!frame_aligned(runtime, count)) {
- result = -EINVAL;
- goto end;
- }
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (!frame_aligned(runtime, count))
+ return -EINVAL;
count = bytes_to_frames(runtime, count);
result = snd_pcm_lib_write(substream, buf, count);
if (result > 0)
result = frames_to_bytes(runtime, result);
- end:
return result;
}
@@ -2844,7 +2842,8 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
@@ -2878,17 +2877,14 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, result = -ENXIO; goto end);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- result = -EBADFD;
- goto end;
- }
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
if (nr_segs > 128 || nr_segs != runtime->channels ||
- !frame_aligned(runtime, iov->iov_len)) {
- result = -EINVAL;
- goto end;
- }
+ !frame_aligned(runtime, iov->iov_len))
+ return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
if (bufs == NULL)
@@ -2899,7 +2895,6 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (result > 0)
result = frames_to_bytes(runtime, result);
kfree(bufs);
- end:
return result;
}
@@ -2914,7 +2909,8 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait);
@@ -2952,7 +2948,8 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait);
@@ -3022,7 +3019,6 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file
if (!(area->vm_flags & VM_READ))
return -EINVAL;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EAGAIN);
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
return -EINVAL;
@@ -3062,7 +3058,6 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
if (!(area->vm_flags & VM_READ))
return -EINVAL;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EAGAIN);
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
return -EINVAL;
@@ -3194,7 +3189,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
return -EINVAL;
}
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EAGAIN);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
@@ -3226,7 +3220,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) {
@@ -3249,14 +3244,17 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
struct snd_pcm_file * pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
- int err;
+ int err = -ENXIO;
+ lock_kernel();
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ goto out;
runtime = substream->runtime;
-
err = fasync_helper(fd, file, on, &runtime->fasync);
+out:
+ unlock_kernel();
if (err < 0)
return err;
return 0;
@@ -3387,6 +3385,17 @@ out:
}
#endif /* CONFIG_SND_SUPPORT_OLD_API */
+#ifndef CONFIG_MMU
+unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ return 0;
+}
+#else
+# define dummy_get_unmapped_area NULL
+#endif
+
/*
* Register section
*/
@@ -3403,6 +3412,7 @@ const struct file_operations snd_pcm_f_ops[2] = {
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
+ .get_unmapped_area = dummy_get_unmapped_area,
},
{
.owner = THIS_MODULE,
@@ -3415,5 +3425,6 @@ const struct file_operations snd_pcm_f_ops[2] = {
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
+ .get_unmapped_area = dummy_get_unmapped_area,
}
};
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 033a024d153..2c89c04f291 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -51,12 +51,14 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
mult = 1000000000;
rate = runtime->rate;
- snd_assert(rate != 0, return);
+ if (snd_BUG_ON(!rate))
+ return;
l = gcd(mult, rate);
mult /= l;
rate /= l;
fsize = runtime->period_size;
- snd_assert(fsize != 0, return);
+ if (snd_BUG_ON(!fsize))
+ return;
l = gcd(rate, fsize);
rate /= l;
fsize /= l;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index f7ea7287c59..c4995c9f573 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -418,7 +418,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
mutex_lock(&rmidi->open_mutex);
while (1) {
subdevice = -1;
- down_read(&card->controls_rwsem);
+ read_lock(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == current->pid) {
subdevice = kctl->prefer_rawmidi_subdevice;
@@ -426,7 +426,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
break;
}
}
- up_read(&card->controls_rwsem);
+ read_unlock(&card->ctl_files_rwlock);
err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device,
subdevice, fflags, rawmidi_file);
if (err >= 0)
@@ -470,8 +470,8 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_runtime *runtime;
- snd_assert(rfile != NULL, return -ENXIO);
- snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO);
+ if (snd_BUG_ON(!rfile))
+ return -ENXIO;
rmidi = rfile->rmidi;
mutex_lock(&rmidi->open_mutex);
if (rfile->input != NULL) {
@@ -1100,7 +1100,7 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
return -EINVAL;
}
spin_lock_irqsave(&runtime->lock, flags);
- snd_assert(runtime->avail + count <= runtime->buffer_size, );
+ snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
runtime->avail += count;
@@ -1141,8 +1141,10 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
long count1, result;
struct snd_rawmidi_runtime *runtime = substream->runtime;
- snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL);
- snd_assert(runtime->buffer != NULL, return -EINVAL);
+ if (snd_BUG_ON(!kernelbuf && !userbuf))
+ return -EINVAL;
+ if (snd_BUG_ON(!runtime->buffer))
+ return -EINVAL;
result = 0;
spin_lock_irqsave(&runtime->lock, flags);
@@ -1420,9 +1422,10 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
.dev_disconnect = snd_rawmidi_dev_disconnect,
};
- snd_assert(rrawmidi != NULL, return -EINVAL);
- *rrawmidi = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rrawmidi)
+ *rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
if (rmidi == NULL) {
snd_printk(KERN_ERR "rawmidi: cannot allocate\n");
@@ -1455,7 +1458,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
snd_rawmidi_free(rmidi);
return err;
}
- *rrawmidi = rmidi;
+ if (rrawmidi)
+ *rrawmidi = rmidi;
return 0;
}
@@ -1472,7 +1476,8 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
{
- snd_assert(rmidi != NULL, return -ENXIO);
+ if (!rmidi)
+ return 0;
snd_info_free_entry(rmidi->proc_entry);
rmidi->proc_entry = NULL;
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
index 97b30fb4c36..51e64e30dd3 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -91,7 +91,8 @@ static int
rtctimer_start(struct snd_timer *timer)
{
rtc_task_t *rtc = timer->private_data;
- snd_assert(rtc != NULL, return -EINVAL);
+ if (snd_BUG_ON(!rtc))
+ return -EINVAL;
rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
rtc_control(rtc, RTC_PIE_ON, 0);
return 0;
@@ -101,7 +102,8 @@ static int
rtctimer_stop(struct snd_timer *timer)
{
rtc_task_t *rtc = timer->private_data;
- snd_assert(rtc != NULL, return -EINVAL);
+ if (snd_BUG_ON(!rtc))
+ return -EINVAL;
rtc_control(rtc, RTC_PIE_OFF, 0);
return 0;
}
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 777796e9449..f25e3cc7ddf 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -164,7 +164,8 @@ odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return -EIO);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_read(dp, buf, count);
}
@@ -174,7 +175,8 @@ odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offs
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return -EIO);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_write(dp, buf, count, file);
}
@@ -183,7 +185,8 @@ odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return -EIO);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_ioctl(dp, cmd, arg);
}
@@ -198,7 +201,8 @@ odev_poll(struct file *file, poll_table * wait)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return 0);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_poll(dp, file, wait);
}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index 558dadbf45f..945a27c34a9 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -308,7 +308,8 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
+ if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+ return;
for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i];
if (! info->opened)
@@ -402,7 +403,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
+ if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
+ return;
info = &dp->synths[dev];
if (! info->opened)
return;
@@ -604,6 +606,9 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
{
struct seq_oss_synth *rec;
+ if (dev < 0 || dev >= dp->max_synthdev)
+ return -ENXIO;
+
if (dp->synths[dev].is_midi) {
struct midi_info minf;
snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 47cfa5186e3..8ca2be339f3 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -148,7 +148,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
return NULL;
}
spin_unlock_irqrestore(&clients_lock, flags);
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
if (!in_interrupt()) {
static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS];
static char card_requested[SNDRV_CARDS];
@@ -266,7 +266,8 @@ static int seq_free_client1(struct snd_seq_client *client)
{
unsigned long flags;
- snd_assert(client != NULL, return -EINVAL);
+ if (!client)
+ return 0;
snd_seq_delete_all_ports(client);
snd_seq_queue_client_leave(client->number);
spin_lock_irqsave(&clients_lock, flags);
@@ -403,7 +404,8 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
return -EFAULT;
/* check client structures are in place */
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
return -ENXIO;
@@ -825,7 +827,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
struct snd_seq_client *client;
int result;
- snd_assert(cell != NULL, return -EINVAL);
+ if (snd_BUG_ON(!cell))
+ return -EINVAL;
client = snd_seq_client_use_ptr(cell->event.source.client);
if (client == NULL) {
@@ -994,7 +997,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
return -ENXIO;
/* check client structures are in place */
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
if (!client->accept_output || client->pool == NULL)
return -ENXIO;
@@ -1076,7 +1080,8 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
unsigned int mask = 0;
/* check client structures are in place */
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
client->data.user.fifo) {
@@ -2195,7 +2200,8 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg
{
struct snd_seq_client *client = file->private_data;
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
}
@@ -2216,7 +2222,8 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
struct snd_seq_client *client;
va_list args;
- snd_assert(! in_interrupt(), return -EBUSY);
+ if (snd_BUG_ON(in_interrupt()))
+ return -EBUSY;
if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD)
return -EINVAL;
@@ -2265,7 +2272,8 @@ int snd_seq_delete_kernel_client(int client)
{
struct snd_seq_client *ptr;
- snd_assert(! in_interrupt(), return -EBUSY);
+ if (snd_BUG_ON(in_interrupt()))
+ return -EBUSY;
ptr = clientptr(client);
if (ptr == NULL)
@@ -2288,7 +2296,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
struct snd_seq_client *cptr;
int result;
- snd_assert(ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ev))
+ return -EINVAL;
if (ev->type == SNDRV_SEQ_EVENT_NONE)
return 0; /* ignore this */
@@ -2354,7 +2363,8 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
struct snd_seq_client *cptr;
int result;
- snd_assert(ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ev))
+ return -EINVAL;
/* fill in client number */
ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 9628c06e4ea..38693f47c26 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -92,7 +92,8 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
struct snd_seq_client *client = file->private_data;
void __user *argp = compat_ptr(arg);
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
switch (cmd) {
case SNDRV_SEQ_IOCTL_PVERSION:
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 2f00ad28a2b..1f997675c89 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -124,7 +124,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
* load all registered drivers (called from seq_clientmgr.c)
*/
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
static int snd_seq_in_init;
void snd_seq_autoload_lock(void)
@@ -140,7 +140,7 @@ void snd_seq_autoload_unlock(void)
void snd_seq_device_load_drivers(void)
{
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
struct ops_list *ops;
/* Calling request_module during module_init()
@@ -187,7 +187,8 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
if (result)
*result = NULL;
- snd_assert(id != NULL, return -EINVAL);
+ if (snd_BUG_ON(!id))
+ return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
@@ -232,7 +233,8 @@ static int snd_seq_device_free(struct snd_seq_device *dev)
{
struct ops_list *ops;
- snd_assert(dev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!dev))
+ return -EINVAL;
ops = find_driver(dev->id, 0);
if (ops == NULL)
@@ -566,7 +568,5 @@ EXPORT_SYMBOL(snd_seq_device_load_drivers);
EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
-#ifdef CONFIG_KMOD
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
-#endif
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 3a94ed021bd..0d75afa786b 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -65,9 +65,11 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
{
struct snd_seq_fifo *f;
- snd_assert(fifo != NULL, return);
+ if (snd_BUG_ON(!fifo))
+ return;
f = *fifo;
- snd_assert(f != NULL, return);
+ if (snd_BUG_ON(!f))
+ return;
*fifo = NULL;
snd_seq_fifo_clear(f);
@@ -116,7 +118,8 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
unsigned long flags;
int err;
- snd_assert(f != NULL, return -EINVAL);
+ if (snd_BUG_ON(!f))
+ return -EINVAL;
snd_use_lock_use(&f->use_lock);
err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
@@ -174,7 +177,8 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
unsigned long flags;
wait_queue_t wait;
- snd_assert(f != NULL, return -EINVAL);
+ if (snd_BUG_ON(!f))
+ return -EINVAL;
*cellp = NULL;
init_waitqueue_entry(&wait, current);
@@ -233,7 +237,8 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
struct snd_seq_pool *newpool, *oldpool;
struct snd_seq_event_cell *cell, *next, *oldhead;
- snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
+ if (snd_BUG_ON(!f || !f->pool))
+ return -EINVAL;
/* allocate new pool */
newpool = snd_seq_pool_new(poolsize);
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 0cf6ac47731..7fb55436287 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -187,9 +187,11 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
unsigned long flags;
struct snd_seq_pool *pool;
- snd_assert(cell != NULL, return);
+ if (snd_BUG_ON(!cell))
+ return;
pool = cell->pool;
- snd_assert(pool != NULL, return);
+ if (snd_BUG_ON(!pool))
+ return;
spin_lock_irqsave(&pool->lock, flags);
free_cell(pool, cell);
@@ -378,7 +380,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
struct snd_seq_event_cell *cellptr;
unsigned long flags;
- snd_assert(pool != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pool))
+ return -EINVAL;
if (pool->ptr) /* should be atomic? */
return 0;
@@ -414,7 +417,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
struct snd_seq_event_cell *ptr;
int max_count = 5 * HZ;
- snd_assert(pool != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pool))
+ return -EINVAL;
/* wait for closing all threads */
spin_lock_irqsave(&pool->lock, flags);
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 99b35360c50..4d26146a62c 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -116,7 +116,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
struct snd_rawmidi_runtime *runtime;
int tmp;
- snd_assert(substream != NULL || buf != NULL, return -EINVAL);
+ if (snd_BUG_ON(!substream || !buf))
+ return -EINVAL;
runtime = substream->runtime;
if ((tmp = runtime->avail) < count) {
snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
@@ -135,7 +136,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
struct snd_rawmidi_substream *substream;
int len;
- snd_assert(msynth != NULL, return -EINVAL);
+ if (snd_BUG_ON(!msynth))
+ return -EINVAL;
substream = msynth->output_rfile.output;
if (substream == NULL)
return -ENODEV;
@@ -210,7 +212,8 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri
int err;
struct seq_midisynth *msynth = private_data;
- snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
+ if (snd_BUG_ON(!msynth->input_rfile.input))
+ return -EINVAL;
err = snd_rawmidi_kernel_release(&msynth->input_rfile);
return err;
}
@@ -247,7 +250,8 @@ static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *in
struct seq_midisynth *msynth = private_data;
unsigned char buf = 0xff; /* MIDI reset */
- snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
+ if (snd_BUG_ON(!msynth->output_rfile.output))
+ return -EINVAL;
/* sending single MIDI reset message to shut the device up */
snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
snd_rawmidi_drain_output(msynth->output_rfile.output);
@@ -285,7 +289,8 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
int device = dev->device;
unsigned int input_count = 0, output_count = 0;
- snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
+ if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
+ return -EINVAL;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 1c32a53d6bd..3bf7d73ac52 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -130,7 +130,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int num = -1;
/* sanity check */
- snd_assert(client, return NULL);
+ if (snd_BUG_ON(!client))
+ return NULL;
if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
@@ -268,8 +269,8 @@ static int port_delete(struct snd_seq_client *client,
if (port->private_free)
port->private_free(port->private_data);
- snd_assert(port->c_src.count == 0,);
- snd_assert(port->c_dest.count == 0,);
+ snd_BUG_ON(port->c_src.count != 0);
+ snd_BUG_ON(port->c_dest.count != 0);
kfree(port);
return 0;
@@ -336,7 +337,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
int snd_seq_set_port_info(struct snd_seq_client_port * port,
struct snd_seq_port_info * info)
{
- snd_assert(port && info, return -EINVAL);
+ if (snd_BUG_ON(!port || !info))
+ return -EINVAL;
/* set port name */
if (info->name[0])
@@ -365,7 +367,8 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
int snd_seq_get_port_info(struct snd_seq_client_port * port,
struct snd_seq_port_info * info)
{
- snd_assert(port && info, return -EINVAL);
+ if (snd_BUG_ON(!port || !info))
+ return -EINVAL;
/* get port name */
strlcpy(info->name, port->name, sizeof(info->name));
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 85969db576c..0101a8b99b7 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -153,8 +153,8 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
int count;
int prior;
- snd_assert(f, return -EINVAL);
- snd_assert(cell, return -EINVAL);
+ if (snd_BUG_ON(!f || !cell))
+ return -EINVAL;
/* check flags */
prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 4a48c6ee8ee..e7a8e9e4edb 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -315,7 +315,8 @@ int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
int dest, err;
struct snd_seq_queue *q;
- snd_assert(cell != NULL, return -EINVAL);
+ if (snd_BUG_ON(!cell))
+ return -EINVAL;
dest = cell->event.queue; /* destination queue */
q = queueptr(dest);
if (q == NULL)
@@ -734,7 +735,8 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
{
struct snd_seq_queue *q;
- snd_assert(ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ev))
+ return -EINVAL;
q = queueptr(ev->data.queue.queue);
if (q == NULL)
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index d8fcd62e400..f745c317d6a 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -173,7 +173,8 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (tempo <= 0)
return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
@@ -190,7 +191,8 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (ppq <= 0)
return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
@@ -214,7 +216,8 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
tmr->tick.cur_tick = position;
@@ -229,7 +232,8 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
snd_seq_sanity_real_time(&position);
spin_lock_irqsave(&tmr->lock, flags);
@@ -244,7 +248,8 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
/* FIXME */
if (base != SKEW_BASE) {
@@ -265,7 +270,8 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
int err;
tmr = q->timer;
- snd_assert(tmr != NULL, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (tmr->timeri)
return -EBUSY;
sprintf(str, "sequencer queue %i", q->queue);
@@ -302,7 +308,8 @@ int snd_seq_timer_close(struct snd_seq_queue *q)
struct snd_seq_timer *tmr;
tmr = q->timer;
- snd_assert(tmr != NULL, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (tmr->timeri) {
snd_timer_stop(tmr->timeri);
snd_timer_close(tmr->timeri);
@@ -328,7 +335,8 @@ static int initialize_timer(struct snd_seq_timer *tmr)
unsigned long freq;
t = tmr->timeri->timer;
- snd_assert(t, return -EINVAL);
+ if (snd_BUG_ON(!t))
+ return -EINVAL;
freq = tmr->preferred_resolution;
if (!freq)
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index cefd228cd2a..d4564edd61d 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -41,9 +41,11 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
tmpb.dev.dev = sgbuf->dev;
for (i = 0; i < sgbuf->pages; i++) {
+ if (!(sgbuf->table[i].addr & ~PAGE_MASK))
+ continue; /* continuous pages */
tmpb.area = sgbuf->table[i].buf;
- tmpb.addr = sgbuf->table[i].addr;
- tmpb.bytes = PAGE_SIZE;
+ tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
+ tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
snd_dma_free_pages(&tmpb);
}
if (dmab->area)
@@ -58,13 +60,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
return 0;
}
+#define MAX_ALLOC_PAGES 32
+
void *snd_malloc_sgbuf_pages(struct device *device,
size_t size, struct snd_dma_buffer *dmab,
size_t *res_size)
{
struct snd_sg_buf *sgbuf;
- unsigned int i, pages;
+ unsigned int i, pages, chunk, maxpages;
struct snd_dma_buffer tmpb;
+ struct snd_sg_page *table;
+ struct page **pgtable;
dmab->area = NULL;
dmab->addr = 0;
@@ -74,31 +80,55 @@ void *snd_malloc_sgbuf_pages(struct device *device,
sgbuf->dev = device;
pages = snd_sgbuf_aligned_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
- sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL);
- if (! sgbuf->table)
+ table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
+ if (!table)
goto _failed;
- sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL);
- if (! sgbuf->page_table)
+ sgbuf->table = table;
+ pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
+ if (!pgtable)
goto _failed;
+ sgbuf->page_table = pgtable;
- /* allocate each page */
- for (i = 0; i < pages; i++) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) {
- if (res_size == NULL)
+ /* allocate pages */
+ maxpages = MAX_ALLOC_PAGES;
+ while (pages > 0) {
+ chunk = pages;
+ /* don't be too eager to take a huge chunk */
+ if (chunk > maxpages)
+ chunk = maxpages;
+ chunk <<= PAGE_SHIFT;
+ if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
+ chunk, &tmpb) < 0) {
+ if (!sgbuf->pages)
+ return NULL;
+ if (!res_size)
goto _failed;
- *res_size = size = sgbuf->pages * PAGE_SIZE;
+ size = sgbuf->pages * PAGE_SIZE;
break;
}
- sgbuf->table[i].buf = tmpb.area;
- sgbuf->table[i].addr = tmpb.addr;
- sgbuf->page_table[i] = virt_to_page(tmpb.area);
- sgbuf->pages++;
+ chunk = tmpb.bytes >> PAGE_SHIFT;
+ for (i = 0; i < chunk; i++) {
+ table->buf = tmpb.area;
+ table->addr = tmpb.addr;
+ if (!i)
+ table->addr |= chunk; /* mark head */
+ table++;
+ *pgtable++ = virt_to_page(tmpb.area);
+ tmpb.area += PAGE_SIZE;
+ tmpb.addr += PAGE_SIZE;
+ }
+ sgbuf->pages += chunk;
+ pages -= chunk;
+ if (chunk < maxpages)
+ maxpages = chunk;
}
sgbuf->size = size;
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
if (! dmab->area)
goto _failed;
+ if (res_size)
+ *res_size = sgbuf->size;
return dmab->area;
_failed:
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 6c8ab48c689..44a69bb8d4f 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/time.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -33,8 +34,6 @@
#include <linux/kmod.h>
#include <linux/mutex.h>
-#define SNDRV_OS_MINORS 256
-
static int major = CONFIG_SND_MAJOR;
int snd_major;
EXPORT_SYMBOL(snd_major);
@@ -60,14 +59,14 @@ EXPORT_SYMBOL(snd_ecards_limit);
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
static DEFINE_MUTEX(sound_mutex);
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
/**
* snd_request_card - try to load the card module
* @card: the card number
*
* Tries to load the module "snd-card-X" for the given card number
- * via KMOD. Returns immediately if already loaded.
+ * via request_module. Returns immediately if already loaded.
*/
void snd_request_card(int card)
{
@@ -92,7 +91,7 @@ static void snd_request_other(int minor)
request_module(str);
}
-#endif /* request_module support */
+#endif /* modular kernel */
/**
* snd_lookup_minor_data - get user data of a registered device
@@ -121,7 +120,7 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
EXPORT_SYMBOL(snd_lookup_minor_data);
-static int snd_open(struct inode *inode, struct file *file)
+static int __snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
@@ -132,7 +131,7 @@ static int snd_open(struct inode *inode, struct file *file)
return -ENODEV;
mptr = snd_minors[minor];
if (mptr == NULL) {
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
int dev = SNDRV_MINOR_DEVICE(minor);
if (dev == SNDRV_MINOR_CONTROL) {
/* /dev/aloadC? */
@@ -163,6 +162,18 @@ static int snd_open(struct inode *inode, struct file *file)
return err;
}
+
+/* BKL pushdown: nasty #ifdef avoidance wrapper */
+static int snd_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ lock_kernel();
+ ret = __snd_open(inode, file);
+ unlock_kernel();
+ return ret;
+}
+
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
@@ -195,20 +206,23 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
minor = type;
break;
case SNDRV_DEVICE_TYPE_CONTROL:
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
minor = SNDRV_MINOR(card->number, type);
break;
case SNDRV_DEVICE_TYPE_HWDEP:
case SNDRV_DEVICE_TYPE_RAWMIDI:
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
minor = SNDRV_MINOR(card->number, type + dev);
break;
default:
return -EINVAL;
}
- snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
+ if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
+ return -EINVAL;
return minor;
}
#endif
@@ -236,7 +250,8 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
int minor;
struct snd_minor *preg;
- snd_assert(name, return -EINVAL);
+ if (snd_BUG_ON(!name))
+ return -EINVAL;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
@@ -259,9 +274,8 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
return minor;
}
snd_minors[minor] = preg;
- preg->dev = device_create_drvdata(sound_class, device,
- MKDEV(major, minor),
- private_data, "%s", name);
+ preg->dev = device_create(sound_class, device, MKDEV(major, minor),
+ private_data, "%s", name);
if (IS_ERR(preg->dev)) {
snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex);
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 7be51546eb9..7fe12264ff8 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -64,7 +64,8 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
switch (type) {
case SNDRV_OSS_DEVICE_TYPE_MIXER:
- snd_assert(card != NULL && dev <= 1, return -EINVAL);
+ if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+ return -EINVAL;
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
break;
case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
@@ -74,11 +75,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
minor = SNDRV_MINOR_OSS_MUSIC;
break;
case SNDRV_OSS_DEVICE_TYPE_PCM:
- snd_assert(card != NULL && dev <= 1, return -EINVAL);
+ if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+ return -EINVAL;
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
break;
case SNDRV_OSS_DEVICE_TYPE_MIDI:
- snd_assert(card != NULL && dev <= 1, return -EINVAL);
+ if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+ return -EINVAL;
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
break;
case SNDRV_OSS_DEVICE_TYPE_DMFM:
@@ -90,7 +93,8 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
default:
return -EINVAL;
}
- snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL);
+ if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OSS_MINORS))
+ return -EINVAL;
return minor;
}
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 9d8184a2c2d..e582face89d 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -146,7 +146,7 @@ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
return NULL;
}
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
static void snd_timer_request(struct snd_timer_id *tid)
{
@@ -259,8 +259,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
/* open a master instance */
mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
-#ifdef CONFIG_KMOD
- if (timer == NULL) {
+#ifdef CONFIG_MODULES
+ if (!timer) {
mutex_unlock(&register_mutex);
snd_timer_request(tid);
mutex_lock(&register_mutex);
@@ -306,7 +306,8 @@ int snd_timer_close(struct snd_timer_instance *timeri)
struct snd_timer *timer = NULL;
struct snd_timer_instance *slave, *tmp;
- snd_assert(timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!timeri))
+ return -ENXIO;
/* force to stop the timer */
snd_timer_stop(timeri);
@@ -385,8 +386,9 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
do_posix_clock_monotonic_gettime(&tstamp);
else
getnstimeofday(&tstamp);
- snd_assert(event >= SNDRV_TIMER_EVENT_START &&
- event <= SNDRV_TIMER_EVENT_PAUSE, return);
+ if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
+ event > SNDRV_TIMER_EVENT_PAUSE))
+ return;
if (event == SNDRV_TIMER_EVENT_START ||
event == SNDRV_TIMER_EVENT_CONTINUE)
resolution = snd_timer_resolution(ti);
@@ -474,7 +476,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
struct snd_timer *timer;
unsigned long flags;
- snd_assert(timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!timeri))
+ return -ENXIO;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
if (!keep_flag) {
@@ -758,9 +761,10 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
.dev_disconnect = snd_timer_dev_disconnect,
};
- snd_assert(tid != NULL, return -EINVAL);
- snd_assert(rtimer != NULL, return -EINVAL);
- *rtimer = NULL;
+ if (snd_BUG_ON(!tid))
+ return -EINVAL;
+ if (rtimer)
+ *rtimer = NULL;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (timer == NULL) {
snd_printk(KERN_ERR "timer: cannot allocate\n");
@@ -788,13 +792,15 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
return err;
}
}
- *rtimer = timer;
+ if (rtimer)
+ *rtimer = timer;
return 0;
}
static int snd_timer_free(struct snd_timer *timer)
{
- snd_assert(timer != NULL, return -ENXIO);
+ if (!timer)
+ return 0;
mutex_lock(&register_mutex);
if (! list_empty(&timer->open_list_head)) {
@@ -827,8 +833,8 @@ static int snd_timer_dev_register(struct snd_device *dev)
struct snd_timer *timer = dev->device_data;
struct snd_timer *timer1;
- snd_assert(timer != NULL && timer->hw.start != NULL &&
- timer->hw.stop != NULL, return -ENXIO);
+ if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
+ return -ENXIO;
if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
!timer->hw.resolution && timer->hw.c_resolution == NULL)
return -EINVAL;
@@ -879,8 +885,9 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
- snd_assert(event >= SNDRV_TIMER_EVENT_MSTART &&
- event <= SNDRV_TIMER_EVENT_MRESUME, return);
+ if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
+ event > SNDRV_TIMER_EVENT_MRESUME))
+ return;
spin_lock_irqsave(&timer->lock, flags);
if (event == SNDRV_TIMER_EVENT_MSTART ||
event == SNDRV_TIMER_EVENT_MCONTINUE ||
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index 5512f5373c5..e05802ae6e1 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -40,9 +40,11 @@ static int snd_timer_user_info_compat(struct file *file,
struct snd_timer *t;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!tu->timeri))
+ return -ENXIO;
t = tu->timeri->timer;
- snd_assert(t != NULL, return -ENXIO);
+ if (snd_BUG_ON(!t))
+ return -ENXIO;
memset(&info, 0, sizeof(info));
info.card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
@@ -71,7 +73,8 @@ static int snd_timer_user_status_compat(struct file *file,
struct snd_timer_status status;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!tu->timeri))
+ return -ENXIO;
memset(&status, 0, sizeof(status));
status.tstamp = tu->tstamp;
status.resolution = snd_timer_resolution(tu->timeri);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 602b58e3b55..255fd18b9ae 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -1,15 +1,41 @@
-# ALSA generic drivers
+config SND_MPU401_UART
+ tristate
+ select SND_RAWMIDI
-menu "Generic devices"
- depends on SND!=n
+config SND_OPL3_LIB
+ tristate
+ select SND_TIMER
+ select SND_HWDEP
+config SND_OPL4_LIB
+ tristate
+ select SND_TIMER
+ select SND_HWDEP
+
+config SND_VX_LIB
+ tristate
+ select SND_HWDEP
+ select SND_PCM
+
+config SND_AC97_CODEC
+ tristate
+ select SND_PCM
+ select AC97_BUS
+ select SND_VMASTER
+
+menuconfig SND_DRIVERS
+ bool "Generic sound devices"
+ default y
+ help
+ Support for generic sound devices.
+
+if SND_DRIVERS
config SND_PCSP
tristate "PC-Speaker support (READ HELP!)"
depends on PCSPKR_PLATFORM && X86_PC && HIGH_RES_TIMERS
depends on INPUT
depends on EXPERIMENTAL
- depends on SND
select SND_PCM
help
If you don't have a sound card in your computer, you can include a
@@ -35,33 +61,8 @@ config SND_PCSP
Say M if you don't.
Say Y only if you really know what you do.
-config SND_MPU401_UART
- tristate
- select SND_RAWMIDI
-
-config SND_OPL3_LIB
- tristate
- select SND_TIMER
- select SND_HWDEP
-
-config SND_OPL4_LIB
- tristate
- select SND_TIMER
- select SND_HWDEP
-
-config SND_VX_LIB
- tristate
- select SND_HWDEP
- select SND_PCM
-
-config SND_AC97_CODEC
- tristate
- select SND_PCM
- select AC97_BUS
-
config SND_DUMMY
tristate "Dummy (/dev/null) soundcard"
- depends on SND
select SND_PCM
help
Say Y here to include the dummy driver. This driver does
@@ -90,7 +91,6 @@ config SND_VIRMIDI
config SND_MTPAV
tristate "MOTU MidiTimePiece AV multiport MIDI"
- depends on SND
select SND_RAWMIDI
help
To use a MOTU MidiTimePiece AV multiport MIDI adapter
@@ -102,7 +102,7 @@ config SND_MTPAV
config SND_MTS64
tristate "ESI Miditerminal 4140 driver"
- depends on SND && PARPORT
+ depends on PARPORT
select SND_RAWMIDI
help
The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with
@@ -115,7 +115,6 @@ config SND_MTS64
config SND_SERIAL_U16550
tristate "UART16550 serial MIDI driver"
- depends on SND
select SND_RAWMIDI
help
To include support for MIDI serial port interfaces, say Y here
@@ -131,7 +130,6 @@ config SND_SERIAL_U16550
config SND_MPU401
tristate "Generic MPU-401 UART driver"
- depends on SND
select SND_MPU401_UART
help
Say Y here to include support for MIDI ports compatible with
@@ -142,7 +140,7 @@ config SND_MPU401
config SND_PORTMAN2X4
tristate "Portman 2x4 driver"
- depends on SND && PARPORT
+ depends on PARPORT
select SND_RAWMIDI
help
Say Y here to include support for Midiman Portman 2x4 parallel
@@ -153,7 +151,7 @@ config SND_PORTMAN2X4
config SND_ML403_AC97CR
tristate "Xilinx ML403 AC97 Controller Reference"
- depends on SND && XILINX_VIRTEX
+ depends on XILINX_VIRTEX
select SND_AC97_CODEC
help
Say Y here to include support for the
@@ -163,4 +161,25 @@ config SND_ML403_AC97CR
To compile this driver as a module, choose M here: the module
will be called snd-ml403_ac97cr.
-endmenu
+config SND_AC97_POWER_SAVE
+ bool "AC97 Power-Saving Mode"
+ depends on SND_AC97_CODEC && EXPERIMENTAL
+ default n
+ help
+ Say Y here to enable the aggressive power-saving support of
+ AC97 codecs. In this mode, the power-mode is dynamically
+ controlled at each open/close.
+
+ The mode is activated by passing power_save=1 option to
+ snd-ac97-codec driver. You can toggle it dynamically over
+ sysfs, too.
+
+config SND_AC97_POWER_SAVE_DEFAULT
+ int "Default time-out for AC97 power-save mode"
+ depends on SND_AC97_POWER_SAVE
+ default 0
+ help
+ The default time-out value in seconds for AC97 automatic
+ power-save mode. 0 means to disable the power-save mode.
+
+endif # SND_DRIVERS
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 4e4c69e6cb4..e5e749f3e0e 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -47,9 +47,11 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
{
int err;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0)
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
+ if (err) < 0)
return err;
return 0;
}
@@ -354,6 +356,7 @@ static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
if ((dpcm = new_pcm_stream(substream)) == NULL)
return -ENOMEM;
runtime->private_data = dpcm;
+ /* makes the infrastructure responsible for freeing dpcm */
runtime->private_free = snd_card_dummy_runtime_free;
runtime->hw = snd_card_dummy_playback;
if (substream->pcm->device & 1) {
@@ -362,10 +365,9 @@ static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
}
if (substream->pcm->device & 2)
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
- if ((err = add_playback_constraints(runtime)) < 0) {
- kfree(dpcm);
+ err = add_playback_constraints(runtime);
+ if (err < 0)
return err;
- }
return 0;
}
@@ -379,6 +381,7 @@ static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
if ((dpcm = new_pcm_stream(substream)) == NULL)
return -ENOMEM;
runtime->private_data = dpcm;
+ /* makes the infrastructure responsible for freeing dpcm */
runtime->private_free = snd_card_dummy_runtime_free;
runtime->hw = snd_card_dummy_capture;
if (substream->pcm->device == 1) {
@@ -387,10 +390,9 @@ static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
}
if (substream->pcm->device & 2)
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
- if ((err = add_capture_constraints(runtime)) < 0) {
- kfree(dpcm);
+ err = add_capture_constraints(runtime);
+ if (err < 0)
return err;
- }
return 0;
}
@@ -433,8 +435,9 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device,
- substreams, substreams, &pcm)) < 0)
+ err = snd_pcm_new(dummy->card, "Dummy PCM", device,
+ substreams, substreams, &pcm);
+ if (err < 0)
return err;
dummy->pcm = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
@@ -565,12 +568,14 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
unsigned int idx;
int err;
- snd_assert(dummy != NULL, return -EINVAL);
+ if (snd_BUG_ON(!dummy))
+ return -EINVAL;
spin_lock_init(&dummy->mixer_lock);
strcpy(card->mixername, "Dummy Mixer");
for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy));
+ if (err < 0)
return err;
}
return 0;
@@ -594,10 +599,12 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
pcm_substreams[dev] = 1;
if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
- if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0)
+ err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]);
+ if (err < 0)
goto __nodev;
}
- if ((err = snd_card_dummy_new_mixer(dummy)) < 0)
+ err = snd_card_dummy_new_mixer(dummy);
+ if (err < 0)
goto __nodev;
strcpy(card->driver, "Dummy");
strcpy(card->shortname, "Dummy");
@@ -605,7 +612,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
snd_card_set_dev(card, &devptr->dev);
- if ((err = snd_card_register(card)) == 0) {
+ err = snd_card_register(card);
+ if (err == 0) {
platform_set_drvdata(devptr, card);
return 0;
}
@@ -668,7 +676,8 @@ static int __init alsa_card_dummy_init(void)
{
int i, cards, err;
- if ((err = platform_driver_register(&snd_dummy_driver)) < 0)
+ err = platform_driver_register(&snd_dummy_driver);
+ if (err < 0)
return err;
cards = 0;
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index b5e1a71bb64..5b89c0883d6 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -715,6 +715,10 @@ static int __devinit snd_mtpav_probe(struct platform_device *dev)
card->private_free = snd_mtpav_free;
+ err = snd_mtpav_get_RAWMIDI(mtp_card);
+ if (err < 0)
+ goto __error;
+
err = snd_mtpav_get_ISA(mtp_card);
if (err < 0)
goto __error;
@@ -724,10 +728,6 @@ static int __devinit snd_mtpav_probe(struct platform_device *dev)
snprintf(card->longname, sizeof(card->longname),
"MTPAV on parallel port at 0x%lx", port);
- err = snd_mtpav_get_RAWMIDI(mtp_card);
- if (err < 0)
- goto __error;
-
snd_mtpav_portscan(mtp_card);
snd_card_set_dev(card, &dev->dev);
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index ebe4359047c..780582340fe 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -139,7 +139,8 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
* If we had an OPL4 chip, opl3->hardware would have been set
* by the OPL4 driver; so we can assume OPL3 here.
*/
- snd_assert(opl3->r_port != 0, return -ENODEV);
+ if (snd_BUG_ON(!opl3->r_port))
+ return -ENODEV;
opl3->hardware = OPL3_HW_OPL3;
}
return 0;
@@ -324,7 +325,8 @@ EXPORT_SYMBOL(snd_opl3_interrupt);
static int snd_opl3_free(struct snd_opl3 *opl3)
{
- snd_assert(opl3 != NULL, return -ENXIO);
+ if (snd_BUG_ON(!opl3))
+ return -ENXIO;
if (opl3->private_free)
opl3->private_free(opl3);
snd_opl3_clear_patches(opl3);
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index cebcb8b78ac..16feafa2c51 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -617,7 +617,8 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
struct snd_opl3_voice *vp, *vp2;
- snd_assert(voice < MAX_OPL3_VOICES, return);
+ if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
+ return;
vp = &opl3->voices[voice];
if (voice < MAX_OPL2_VOICES) {
@@ -737,7 +738,8 @@ static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)
struct snd_opl3_voice *vp;
- snd_assert(voice < MAX_OPL3_VOICES, return);
+ if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
+ return;
vp = &opl3->voices[voice];
if (vp->chan == NULL)
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index 239347f2615..9a2271dc046 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -162,7 +162,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
struct snd_opl3 *opl3 = closure;
int err;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
if ((err = snd_opl3_synth_setup(opl3)) < 0)
return err;
@@ -184,7 +185,8 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
{
struct snd_opl3 *opl3;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
snd_opl3_synth_cleanup(opl3);
@@ -206,7 +208,8 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
char name[32];
int err, type;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
if (format == FM_PATCH)
@@ -246,7 +249,8 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
{
struct snd_opl3 *opl3;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
switch (cmd) {
case SNDCTL_FM_LOAD_INSTR:
@@ -271,7 +275,8 @@ static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)
{
struct snd_opl3 *opl3;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
return 0;
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index fb64c890109..962bb9c8b9c 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -92,7 +92,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
struct snd_opl3 *opl3 = hw->private_data;
void __user *argp = (void __user *)arg;
- snd_assert(opl3 != NULL, return -EINVAL);
+ if (snd_BUG_ON(!opl3))
+ return -EINVAL;
switch (cmd) {
/* get information */
diff --git a/sound/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c
index 74f6e53eae0..49b9e240915 100644
--- a/sound/drivers/opl4/opl4_synth.c
+++ b/sound/drivers/opl4/opl4_synth.c
@@ -467,7 +467,7 @@ static struct opl4_voice *snd_opl4_get_voice(struct snd_opl4 *opl4)
if (!list_empty(&opl4->off_voices))
return list_entry(opl4->off_voices.next, struct opl4_voice, list);
/* then get the oldest key-on voice */
- snd_assert(!list_empty(&opl4->on_voices), );
+ snd_BUG_ON(list_empty(&opl4->on_voices));
return list_entry(opl4->on_voices.next, struct opl4_voice, list);
}
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c
index 9529e3bf286..23f4857f02c 100644
--- a/sound/drivers/vx/vx_cmd.c
+++ b/sound/drivers/vx/vx_cmd.c
@@ -99,7 +99,8 @@ static struct vx_cmd_info vx_dsp_cmds[] = {
*/
void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd)
{
- snd_assert(cmd < CMD_LAST_INDEX, return);
+ if (snd_BUG_ON(cmd >= CMD_LAST_INDEX))
+ return;
rmh->LgCmd = vx_dsp_cmds[cmd].length;
rmh->LgStat = vx_dsp_cmds[cmd].st_length;
rmh->DspStat = vx_dsp_cmds[cmd].st_type;
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 99538862e34..473b07f6ae8 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -205,7 +205,8 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
if (size < 1)
return 0;
- snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL);
+ if (snd_BUG_ON(size > SIZE_MAX_STATUS))
+ return -EINVAL;
for (i = 1; i <= size; i++) {
/* trigger an irq MESS_WRITE_NEXT */
@@ -425,13 +426,16 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
int no_fillup = vx_has_new_dsp(chip);
/* check the length of boot image */
- snd_assert(boot->size > 0, return -EINVAL);
- snd_assert(boot->size % 3 == 0, return -EINVAL);
+ if (boot->size <= 0)
+ return -EINVAL;
+ if (boot->size % 3)
+ return -EINVAL;
#if 0
{
/* more strict check */
unsigned int c = ((u32)boot->data[0] << 16) | ((u32)boot->data[1] << 8) | boot->data[2];
- snd_assert(boot->size == (c + 2) * 3, return -EINVAL);
+ if (boot->size != (c + 2) * 3)
+ return -EINVAL;
}
#endif
@@ -453,7 +457,7 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
vx_outb(chip, TXM, 0);
vx_outb(chip, TXL, 0);
} else {
- unsigned char *image = boot->data + i;
+ const unsigned char *image = boot->data + i;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
return -EIO;
@@ -554,7 +558,8 @@ EXPORT_SYMBOL(snd_vx_irq_handler);
*/
static void vx_reset_board(struct vx_core *chip, int cold_reset)
{
- snd_assert(chip->ops->reset_board, return);
+ if (snd_BUG_ON(!chip->ops->reset_board))
+ return;
/* current source, later sync'ed with target */
chip->audio_source = VX_AUDIO_SRC_LINE;
@@ -671,9 +676,10 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
unsigned int i;
int err;
unsigned int csum = 0;
- unsigned char *image, *cptr;
+ const unsigned char *image, *cptr;
- snd_assert(dsp->size % 3 == 0, return -EINVAL);
+ if (dsp->size % 3)
+ return -EINVAL;
vx_toggle_dac_mute(chip, 1);
@@ -775,7 +781,8 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
{
struct vx_core *chip;
- snd_assert(card && hw && ops, return NULL);
+ if (snd_BUG_ON(!card || !hw || !ops))
+ return NULL;
chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);
if (! chip) {
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index 1dfe6948e6f..8d6362e2d4c 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -141,7 +141,8 @@ static int vx_hwdep_dsp_status(struct snd_hwdep *hw,
};
struct vx_core *vx = hw->private_data;
- snd_assert(type_ids[vx->type], return -EINVAL);
+ if (snd_BUG_ON(!type_ids[vx->type]))
+ return -EINVAL;
strcpy(info->id, type_ids[vx->type]);
if (vx_is_pcmcia(vx))
info->num_dsps = 4;
@@ -168,7 +169,8 @@ static int vx_hwdep_dsp_load(struct snd_hwdep *hw,
int index, err;
struct firmware *fw;
- snd_assert(vx->ops->load_dsp, return -ENXIO);
+ if (snd_BUG_ON(!vx->ops->load_dsp))
+ return -ENXIO;
fw = kmalloc(sizeof(*fw), GFP_KERNEL);
if (! fw) {
@@ -183,7 +185,7 @@ static int vx_hwdep_dsp_load(struct snd_hwdep *hw,
kfree(fw);
return -ENOMEM;
}
- if (copy_from_user(fw->data, dsp->image, dsp->length)) {
+ if (copy_from_user((void *)fw->data, dsp->image, dsp->length)) {
free_fw(fw);
return -EFAULT;
}
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index 5a347321f8c..c71b8d148d7 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -34,7 +34,8 @@ static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int dat
{
unsigned long flags;
- snd_assert(chip->ops->write_codec, return);
+ if (snd_BUG_ON(!chip->ops->write_codec))
+ return;
if (chip->chip_status & VX_STAT_IS_STALE)
return;
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index fdbf86571b1..27de574c08f 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -587,7 +587,8 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs)
return -EBUSY;
audio = subs->pcm->device * 2;
- snd_assert(audio < chip->audio_outs, return -EINVAL);
+ if (snd_BUG_ON(audio >= chip->audio_outs))
+ return -EINVAL;
/* playback pipe may have been already allocated for monitoring */
pipe = chip->playback_pipes[audio];
@@ -996,7 +997,8 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs)
return -EBUSY;
audio = subs->pcm->device * 2;
- snd_assert(audio < chip->audio_ins, return -EINVAL);
+ if (snd_BUG_ON(audio >= chip->audio_ins))
+ return -EINVAL;
err = vx_alloc_pipe(chip, 1, audio, 2, &pipe);
if (err < 0)
return err;
@@ -1214,7 +1216,8 @@ void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)
}
if (capture)
continue;
- snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,);
+ if (snd_BUG_ON(p < 0 || p >= chip->audio_outs))
+ continue;
pipe = chip->playback_pipes[p];
if (pipe && pipe->substream) {
vx_pcm_playback_update(chip, pipe->substream, pipe);
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index fb8932af888..0e1ba9b4790 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -163,13 +163,15 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq)
{
int hexfreq;
- snd_assert(freq > 0, return 0);
+ if (snd_BUG_ON(freq <= 0))
+ return 0;
hexfreq = (28224000 * 10) / freq;
hexfreq = (hexfreq + 5) / 10;
/* max freq = 55125 Hz */
- snd_assert(hexfreq > 0x00000200, return 0);
+ if (snd_BUG_ON(hexfreq <= 0x00000200))
+ return 0;
if (hexfreq <= 0x03ff)
return hexfreq - 0x00000201;
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index e57e9cbe6a0..020a5d51247 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <asm/unaligned.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -264,10 +265,7 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
goto __fail;
}
/* write default channel status bytes */
- buf[0] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0));
- buf[1] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8));
- buf[2] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16));
- buf[3] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24));
+ put_unaligned_le32(SNDRV_PCM_DEFAULT_CON_SPDIF, buf);
memset(buf + 4, 0, 24 - 4);
if (snd_cs8427_send_corudata(device, 0, buf, 24) < 0)
goto __fail;
@@ -316,7 +314,8 @@ static void snd_cs8427_reset(struct snd_i2c_device *cs8427)
unsigned long end_time;
int data, aes3input = 0;
- snd_assert(cs8427, return);
+ if (snd_BUG_ON(!cs8427))
+ return;
chip = cs8427->private_data;
snd_i2c_lock(cs8427->bus);
if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) ==
@@ -528,7 +527,8 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,
unsigned int idx;
int err;
- snd_assert(play_substream && cap_substream, return -EINVAL);
+ if (snd_BUG_ON(!play_substream || !cap_substream))
+ return -EINVAL;
for (idx = 0; idx < ARRAY_SIZE(snd_cs8427_iec958_controls); idx++) {
kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427);
if (kctl == NULL)
@@ -545,7 +545,8 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,
chip->playback.substream = play_substream;
chip->capture.substream = cap_substream;
- snd_assert(chip->playback.pcm_ctl, return -EIO);
+ if (snd_BUG_ON(!chip->playback.pcm_ctl))
+ return -EIO;
return 0;
}
@@ -555,7 +556,8 @@ int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active)
{
struct cs8427 *chip;
- snd_assert(cs8427, return -ENXIO);
+ if (snd_BUG_ON(!cs8427))
+ return -ENXIO;
chip = cs8427->private_data;
if (active)
memcpy(chip->playback.pcm_status,
@@ -575,7 +577,8 @@ int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate)
char *status;
int err, reset;
- snd_assert(cs8427, return -ENXIO);
+ if (snd_BUG_ON(!cs8427))
+ return -ENXIO;
chip = cs8427->private_data;
status = chip->playback.pcm_status;
snd_i2c_lock(cs8427->bus);
diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
index b1e74e40cba..5c0c77dd01c 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -49,7 +49,8 @@ static int snd_i2c_bus_free(struct snd_i2c_bus *bus)
struct snd_i2c_bus *slave;
struct snd_i2c_device *device;
- snd_assert(bus != NULL, return -EINVAL);
+ if (snd_BUG_ON(!bus))
+ return -EINVAL;
while (!list_empty(&bus->devices)) {
device = snd_i2c_device(bus->devices.next);
snd_i2c_device_free(device);
@@ -113,7 +114,8 @@ int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,
struct snd_i2c_device *device;
*rdevice = NULL;
- snd_assert(bus != NULL, return -EINVAL);
+ if (snd_BUG_ON(!bus))
+ return -EINVAL;
device = kzalloc(sizeof(*device), GFP_KERNEL);
if (device == NULL)
return -ENOMEM;
diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
index bfa5d2c3608..9840eb43648 100644
--- a/sound/i2c/l3/uda1341.c
+++ b/sound/i2c/l3/uda1341.c
@@ -17,8 +17,6 @@
* 2002-05-12 Tomas Kasparek another code cleanup
*/
-/* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */
-
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -773,7 +771,8 @@ int __init snd_chip_uda1341_mixer_new(struct snd_card *card, struct l3_client **
struct l3_client *clnt;
int idx, err;
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
if (clnt == NULL)
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index d20d893b3b6..0341451f814 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -475,7 +475,8 @@ int snd_ak4114_build(struct ak4114 *ak4114,
unsigned int idx;
int err;
- snd_assert(cap_substream, return -EINVAL);
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
ak4114->playback_substream = ply_substream;
ak4114->capture_substream = cap_substream;
for (idx = 0; idx < AK4114_CONTROLS; idx++) {
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index f350835ade9..2cad2d61251 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -431,7 +431,8 @@ int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *cap_substr
unsigned int idx;
int err;
- snd_assert(cap_substream, return -EINVAL);
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
ak4117->substream = cap_substream;
for (idx = 0; idx < AK4117_CONTROLS; idx++) {
kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 288926d2e20..ee47abab764 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -233,8 +233,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
- 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
- // 0x02, 0x2e, /* quad speed */
+ 0x02, 0x4e, /* 2: DA's power up, normal speed, RSTN#=0 */
+ /* 0x02, 0x6e,*/ /* quad speed */
0x03, 0x01, /* 3: de-emphasis off */
0x04, 0x00, /* 4: LOUT1 volume muted */
0x05, 0x00, /* 5: ROUT1 volume muted */
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index 87e3aefeddc..c13a178383b 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -87,8 +87,7 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea)
static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long data)
{
- struct video_device *dev = video_devdata(file);
- struct snd_tea575x *tea = video_get_drvdata(dev);
+ struct snd_tea575x *tea = video_drvdata(file);
void __user *arg = (void __user *)data;
switch(cmd) {
@@ -175,6 +174,21 @@ static void snd_tea575x_release(struct video_device *vfd)
{
}
+static int snd_tea575x_exclusive_open(struct inode *inode, struct file *file)
+{
+ struct snd_tea575x *tea = video_drvdata(file);
+
+ return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0;
+}
+
+static int snd_tea575x_exclusive_release(struct inode *inode, struct file *file)
+{
+ struct snd_tea575x *tea = video_drvdata(file);
+
+ clear_bit(0, &tea->in_use);
+ return 0;
+}
+
/*
* initialize all the tea575x chips
*/
@@ -189,15 +203,14 @@ void snd_tea575x_init(struct snd_tea575x *tea)
}
memset(&tea->vd, 0, sizeof(tea->vd));
- tea->vd.owner = tea->card->module;
strcpy(tea->vd.name, tea->tea5759 ? "TEA5759 radio" : "TEA5757 radio");
- tea->vd.type = VID_TYPE_TUNER;
tea->vd.release = snd_tea575x_release;
video_set_drvdata(&tea->vd, tea);
tea->vd.fops = &tea->fops;
+ tea->in_use = 0;
tea->fops.owner = tea->card->module;
- tea->fops.open = video_exclusive_open;
- tea->fops.release = video_exclusive_release;
+ tea->fops.open = snd_tea575x_exclusive_open;
+ tea->fops.release = snd_tea575x_exclusive_release;
tea->fops.ioctl = snd_tea575x_ioctl;
if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) {
snd_printk(KERN_ERR "unable to register tea575x tuner\n");
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 2639a6ab8f2..660beb41f76 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -1,10 +1,6 @@
# ALSA ISA drivers
-config SND_AD1848_LIB
- tristate
- select SND_PCM
-
-config SND_CS4231_LIB
+config SND_WSS_LIB
tristate
select SND_PCM
@@ -21,12 +17,17 @@ config SND_SB16_DSP
select SND_PCM
select SND_SB_COMMON
-menu "ISA devices"
- depends on SND!=n && ISA && ISA_DMA_API
+menuconfig SND_ISA
+ bool "ISA sound devices"
+ depends on ISA && ISA_DMA_API
+ default y
+ help
+ Support for sound devices connected via the ISA bus.
+
+if SND_ISA
config SND_ADLIB
tristate "AdLib FM card"
- depends on SND
select SND_OPL3_LIB
help
Say Y here to include support for AdLib FM cards.
@@ -36,7 +37,7 @@ config SND_ADLIB
config SND_AD1816A
tristate "Analog Devices SoundPort AD1816A"
- depends on SND && PNP && ISA
+ depends on PNP
select ISAPNP
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -50,8 +51,7 @@ config SND_AD1816A
config SND_AD1848
tristate "Generic AD1848/CS4248 driver"
- depends on SND
- select SND_AD1848_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for AD1848 (Analog Devices) or
CS4248 (Cirrus Logic - Crystal Semiconductors) chips.
@@ -64,7 +64,7 @@ config SND_AD1848
config SND_ALS100
tristate "Avance Logic ALS100/ALS120"
- depends on SND && PNP && ISA
+ depends on PNP
select ISAPNP
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -78,11 +78,11 @@ config SND_ALS100
config SND_AZT2320
tristate "Aztech Systems AZT2320"
- depends on SND && PNP && ISA
+ depends on PNP
select ISAPNP
select SND_OPL3_LIB
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for soundcards based on the
Aztech Systems AZT2320 chip.
@@ -92,8 +92,7 @@ config SND_AZT2320
config SND_CMI8330
tristate "C-Media CMI8330"
- depends on SND
- select SND_AD1848_LIB
+ select SND_WSS_LIB
select SND_SB16_DSP
help
Say Y here to include support for soundcards based on the
@@ -104,9 +103,8 @@ config SND_CMI8330
config SND_CS4231
tristate "Generic Cirrus Logic CS4231 driver"
- depends on SND
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for CS4231 chips from Cirrus
Logic - Crystal Semiconductors.
@@ -116,10 +114,9 @@ config SND_CS4231
config SND_CS4232
tristate "Generic Cirrus Logic CS4232 driver"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for CS4232 chips from Cirrus
Logic - Crystal Semiconductors.
@@ -129,10 +126,9 @@ config SND_CS4232
config SND_CS4236
tristate "Generic Cirrus Logic CS4236+ driver"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y to include support for CS4235,CS4236,CS4237B,CS4238B,
CS4239 chips from Cirrus Logic - Crystal Semiconductors.
@@ -142,7 +138,7 @@ config SND_CS4236
config SND_DT019X
tristate "Diamond Technologies DT-019X, Avance Logic ALS-007"
- depends on SND && PNP && ISA
+ depends on PNP
select ISAPNP
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -156,7 +152,7 @@ config SND_DT019X
config SND_ES968
tristate "Generic ESS ES968 driver"
- depends on SND && PNP && ISA
+ depends on PNP
select ISAPNP
select SND_MPU401_UART
select SND_SB8_DSP
@@ -168,7 +164,6 @@ config SND_ES968
config SND_ES1688
tristate "Generic ESS ES688/ES1688 driver"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -181,7 +176,6 @@ config SND_ES1688
config SND_ES18XX
tristate "Generic ESS ES18xx driver"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -193,8 +187,8 @@ config SND_ES18XX
config SND_SC6000
tristate "Gallant SC-6000, Audio Excel DSP 16"
- depends on SND && HAS_IOPORT
- select SND_AD1848_LIB
+ depends on HAS_IOPORT
+ select SND_WSS_LIB
select SND_OPL3_LIB
select SND_MPU401_UART
help
@@ -204,15 +198,10 @@ config SND_SC6000
To compile this driver as a module, choose M here: the module
will be called snd-sc6000.
-config SND_GUS_SYNTH
- tristate
-
config SND_GUSCLASSIC
tristate "Gravis UltraSound Classic"
- depends on SND
select SND_RAWMIDI
select SND_PCM
- select SND_GUS_SYNTH
help
Say Y here to include support for Gravis UltraSound Classic
soundcards.
@@ -222,11 +211,9 @@ config SND_GUSCLASSIC
config SND_GUSEXTREME
tristate "Gravis UltraSound Extreme"
- depends on SND
select SND_HWDEP
select SND_MPU401_UART
select SND_PCM
- select SND_GUS_SYNTH
help
Say Y here to include support for Gravis UltraSound Extreme
soundcards.
@@ -236,10 +223,8 @@ config SND_GUSEXTREME
config SND_GUSMAX
tristate "Gravis UltraSound MAX"
- depends on SND
select SND_RAWMIDI
- select SND_CS4231_LIB
- select SND_GUS_SYNTH
+ select SND_WSS_LIB
help
Say Y here to include support for Gravis UltraSound MAX
soundcards.
@@ -249,10 +234,9 @@ config SND_GUSMAX
config SND_INTERWAVE
tristate "AMD InterWave, Gravis UltraSound PnP"
- depends on SND && PNP && ISA
+ depends on PNP
select SND_RAWMIDI
- select SND_CS4231_LIB
- select SND_GUS_SYNTH
+ select SND_WSS_LIB
help
Say Y here to include support for AMD InterWave based
soundcards (Gravis UltraSound Plug & Play, STB SoundRage32,
@@ -263,10 +247,9 @@ config SND_INTERWAVE
config SND_INTERWAVE_STB
tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)"
- depends on SND && PNP && ISA
+ depends on PNP
select SND_RAWMIDI
- select SND_CS4231_LIB
- select SND_GUS_SYNTH
+ select SND_WSS_LIB
help
Say Y here to include support for AMD InterWave based
soundcards with a TEA6330T bass and treble regulator
@@ -277,10 +260,9 @@ config SND_INTERWAVE_STB
config SND_OPL3SA2
tristate "Yamaha OPL3-SA2/SA3"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for Yamaha OPL3-SA2 and OPL3-SA3
chips.
@@ -290,11 +272,10 @@ config SND_OPL3SA2
config SND_OPTI92X_AD1848
tristate "OPTi 82C92x - AD1848"
- depends on SND
select SND_OPL3_LIB
select SND_OPL4_LIB
select SND_MPU401_UART
- select SND_AD1848_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for soundcards based on Opti
82C92x or OTI-601 chips and using an AD1848 codec.
@@ -304,11 +285,10 @@ config SND_OPTI92X_AD1848
config SND_OPTI92X_CS4231
tristate "OPTi 82C92x - CS4231"
- depends on SND
select SND_OPL3_LIB
select SND_OPL4_LIB
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for soundcards based on Opti
82C92x chips and using a CS4231 codec.
@@ -318,10 +298,9 @@ config SND_OPTI92X_CS4231
config SND_OPTI93X
tristate "OPTi 82C93x"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
- select SND_PCM
+ select SND_WSS_LIB
help
Say Y here to include support for soundcards based on Opti
82C93x chips.
@@ -331,9 +310,8 @@ config SND_OPTI93X
config SND_MIRO
tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
- depends on SND
select SND_OPL4_LIB
- select SND_CS4231_LIB
+ select SND_WSS_LIB
select SND_MPU401_UART
select SND_PCM
help
@@ -345,7 +323,6 @@ config SND_MIRO
config SND_SB8
tristate "Sound Blaster 1.0/2.0/Pro (8-bit)"
- depends on SND
select SND_OPL3_LIB
select SND_RAWMIDI
select SND_SB8_DSP
@@ -358,7 +335,6 @@ config SND_SB8
config SND_SB16
tristate "Sound Blaster 16 (PnP)"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_SB16_DSP
@@ -371,7 +347,6 @@ config SND_SB16
config SND_SBAWE
tristate "Sound Blaster AWE (32,64) (PnP)"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_SB16_DSP
@@ -385,25 +360,15 @@ config SND_SBAWE
config SND_SB16_CSP
bool "Sound Blaster 16/AWE CSP support"
depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC)
- select FW_LOADER if !SND_SB16_CSP_FIRMWARE_IN_KERNEL
+ select FW_LOADER
help
Say Y here to include support for the CSP core. This special
coprocessor can do variable tasks like various compression and
decompression algorithms.
-config SND_SB16_CSP_FIRMWARE_IN_KERNEL
- bool "In-kernel firmware for SB16 CSP"
- depends on SND_SB16_CSP
- default y
- help
- Say Y here to include the static firmware built in the kernel
- for the SB16 CSP controller. If you choose N here, you need
- to install the firmware files from the alsa-firmware package.
-
config SND_SGALAXY
tristate "Aztech Sound Galaxy"
- depends on SND
- select SND_AD1848_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for Aztech Sound Galaxy
soundcards.
@@ -413,10 +378,9 @@ config SND_SGALAXY
config SND_SSCAPE
tristate "Ensoniq SoundScape PnP driver"
- depends on SND
select SND_HWDEP
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for Ensoniq SoundScape PnP
soundcards.
@@ -426,11 +390,10 @@ config SND_SSCAPE
config SND_WAVEFRONT
tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)"
- depends on SND
select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
- select SND_CS4231_LIB
+ select SND_WSS_LIB
help
Say Y here to include support for Turtle Beach Maui, Tropez
and Tropez+ soundcards based on the Wavefront chip.
@@ -448,4 +411,5 @@ config SND_WAVEFRONT_FIRMWARE_IN_KERNEL
you need to install the firmware files from the
alsa-firmware package.
-endmenu
+endif # SND_ISA
+
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index c0ce7db2a1b..63af13d901a 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -27,4 +27,4 @@ obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \
- sb/ wavefront/
+ sb/ wavefront/ wss/
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 68f1260b560..77524244a84 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -83,8 +83,10 @@ static struct pnp_card_device_id snd_ad1816a_pnpids[] = {
{ .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
/* Shark Predator ISA - added by Ken Arromdee */
{ .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
- /* Analog Devices AD1816A - Terratec AudioSystem EWS64S */
+ /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
{ .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
+ /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
+ { .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } },
/* Analog Devices AD1816A - Terratec Base 64 */
{ .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
/* end */
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index 4b8dfe2e3dc..3bfca7c59ba 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -394,7 +394,8 @@ static int snd_ad1816a_timer_open(struct snd_timer *timer)
static unsigned long snd_ad1816a_timer_resolution(struct snd_timer *timer)
{
- snd_assert(timer != NULL, return 0);
+ if (snd_BUG_ON(!timer))
+ return 0;
return 10000;
}
@@ -961,7 +962,8 @@ int __devinit snd_ad1816a_mixer(struct snd_ad1816a *chip)
unsigned int idx;
int err;
- snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip || !chip->card))
+ return -EINVAL;
card = chip->card;
diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile
index ae23331e920..3d6dea3ff92 100644
--- a/sound/isa/ad1848/Makefile
+++ b/sound/isa/ad1848/Makefile
@@ -3,10 +3,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1848-lib-objs := ad1848_lib.o
snd-ad1848-objs := ad1848.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1848) += snd-ad1848.o
-obj-$(CONFIG_SND_AD1848_LIB) += snd-ad1848-lib.o
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index 5f5271efdc5..b68d20edc20 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -28,7 +28,7 @@
#include <linux/wait.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
#include <sound/initval.h>
#define CRD_NAME "Generic AD1848/AD1847/CS4248"
@@ -87,7 +87,7 @@ static int __devinit snd_ad1848_match(struct device *dev, unsigned int n)
static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
- struct snd_ad1848 *chip;
+ struct snd_wss *chip;
struct snd_pcm *pcm;
int error;
@@ -95,18 +95,19 @@ static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
if (!card)
return -EINVAL;
- error = snd_ad1848_create(card, port[n], irq[n], dma1[n],
- thinkpad[n] ? AD1848_HW_THINKPAD : AD1848_HW_DETECT, &chip);
+ error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], -1,
+ thinkpad[n] ? WSS_HW_THINKPAD : WSS_HW_DETECT,
+ 0, &chip);
if (error < 0)
goto out;
card->private_data = chip;
- error = snd_ad1848_pcm(chip, 0, &pcm);
+ error = snd_wss_pcm(chip, 0, &pcm);
if (error < 0)
goto out;
- error = snd_ad1848_mixer(chip);
+ error = snd_wss_mixer(chip);
if (error < 0)
goto out;
@@ -142,7 +143,7 @@ static int __devexit snd_ad1848_remove(struct device *dev, unsigned int n)
static int snd_ad1848_suspend(struct device *dev, unsigned int n, pm_message_t state)
{
struct snd_card *card = dev_get_drvdata(dev);
- struct snd_ad1848 *chip = card->private_data;
+ struct snd_wss *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->suspend(chip);
@@ -152,7 +153,7 @@ static int snd_ad1848_suspend(struct device *dev, unsigned int n, pm_message_t s
static int snd_ad1848_resume(struct device *dev, unsigned int n)
{
struct snd_card *card = dev_get_drvdata(dev);
- struct snd_ad1848 *chip = card->private_data;
+ struct snd_wss *chip = card->private_data;
chip->resume(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
deleted file mode 100644
index 630c90f9ee5..00000000000
--- a/sound/isa/ad1848/ad1848_lib.c
+++ /dev/null
@@ -1,1267 +0,0 @@
-/*
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Routines for control of AD1848/AD1847/CS4248
- *
- *
- * 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 SNDRV_MAIN_OBJECT_FILE
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <sound/core.h>
-#include <sound/ad1848.h>
-#include <sound/control.h>
-#include <sound/tlv.h>
-#include <sound/pcm_params.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248");
-MODULE_LICENSE("GPL");
-
-#if 0
-#define SNDRV_DEBUG_MCE
-#endif
-
-/*
- * Some variables
- */
-
-static unsigned char freq_bits[14] = {
- /* 5510 */ 0x00 | AD1848_XTAL2,
- /* 6620 */ 0x0E | AD1848_XTAL2,
- /* 8000 */ 0x00 | AD1848_XTAL1,
- /* 9600 */ 0x0E | AD1848_XTAL1,
- /* 11025 */ 0x02 | AD1848_XTAL2,
- /* 16000 */ 0x02 | AD1848_XTAL1,
- /* 18900 */ 0x04 | AD1848_XTAL2,
- /* 22050 */ 0x06 | AD1848_XTAL2,
- /* 27042 */ 0x04 | AD1848_XTAL1,
- /* 32000 */ 0x06 | AD1848_XTAL1,
- /* 33075 */ 0x0C | AD1848_XTAL2,
- /* 37800 */ 0x08 | AD1848_XTAL2,
- /* 44100 */ 0x0A | AD1848_XTAL2,
- /* 48000 */ 0x0C | AD1848_XTAL1
-};
-
-static unsigned int rates[14] = {
- 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
- 27042, 32000, 33075, 37800, 44100, 48000
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static unsigned char snd_ad1848_original_image[16] =
-{
- 0x00, /* 00 - lic */
- 0x00, /* 01 - ric */
- 0x9f, /* 02 - la1ic */
- 0x9f, /* 03 - ra1ic */
- 0x9f, /* 04 - la2ic */
- 0x9f, /* 05 - ra2ic */
- 0xbf, /* 06 - loc */
- 0xbf, /* 07 - roc */
- 0x20, /* 08 - dfr */
- AD1848_AUTOCALIB, /* 09 - ic */
- 0x00, /* 0a - pc */
- 0x00, /* 0b - ti */
- 0x00, /* 0c - mi */
- 0x00, /* 0d - lbc */
- 0x00, /* 0e - dru */
- 0x00, /* 0f - drl */
-};
-
-/*
- * Basic I/O functions
- */
-
-static void snd_ad1848_wait(struct snd_ad1848 *chip)
-{
- int timeout;
-
- for (timeout = 250; timeout > 0; timeout--) {
- if ((inb(AD1848P(chip, REGSEL)) & AD1848_INIT) == 0)
- break;
- udelay(100);
- }
-}
-
-void snd_ad1848_out(struct snd_ad1848 *chip,
- unsigned char reg,
- unsigned char value)
-{
- snd_ad1848_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
- snd_printk(KERN_WARNING "auto calibration time out - "
- "reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
- outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
- outb(chip->image[reg] = value, AD1848P(chip, REG));
- mb();
- snd_printdd("codec out - reg 0x%x = 0x%x\n",
- chip->mce_bit | reg, value);
-}
-
-EXPORT_SYMBOL(snd_ad1848_out);
-
-static void snd_ad1848_dout(struct snd_ad1848 *chip,
- unsigned char reg, unsigned char value)
-{
- snd_ad1848_wait(chip);
- outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
- outb(value, AD1848P(chip, REG));
- mb();
-}
-
-static unsigned char snd_ad1848_in(struct snd_ad1848 *chip, unsigned char reg)
-{
- snd_ad1848_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
- snd_printk(KERN_WARNING "auto calibration time out - "
- "reg = 0x%x\n", reg);
-#endif
- outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
- mb();
- return inb(AD1848P(chip, REG));
-}
-
-#if 0
-
-static void snd_ad1848_debug(struct snd_ad1848 *chip)
-{
- printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL)));
- printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS)));
- printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00));
- printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));
- printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01));
- printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09));
- printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02));
- printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a));
- printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03));
- printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b));
- printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04));
- printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));
- printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05));
- printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d));
- printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06));
- printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e));
- printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07));
- printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f));
-}
-
-#endif
-
-/*
- * AD1848 detection / MCE routines
- */
-
-static void snd_ad1848_mce_up(struct snd_ad1848 *chip)
-{
- unsigned long flags;
- int timeout;
-
- snd_ad1848_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
- snd_printk(KERN_WARNING "mce_up - auto calibration time out (0)\n");
-#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit |= AD1848_MCE;
- timeout = inb(AD1848P(chip, REGSEL));
- if (timeout == 0x80)
- snd_printk(KERN_WARNING "mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
- if (!(timeout & AD1848_MCE))
- outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
-{
- unsigned long flags, timeout;
- int reg;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (timeout = 5; timeout > 0; timeout--)
- inb(AD1848P(chip, REGSEL));
- /* end of cleanup sequence */
- for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
- udelay(100);
-
- snd_printdd("(1) timeout = %ld\n", timeout);
-
-#ifdef CONFIG_SND_DEBUG
- if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
- snd_printk(KERN_WARNING "mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL));
-#endif
-
- chip->mce_bit &= ~AD1848_MCE;
- reg = inb(AD1848P(chip, REGSEL));
- outb(chip->mce_bit | (reg & 0x1f), AD1848P(chip, REGSEL));
- if (reg == 0x80)
- snd_printk(KERN_WARNING "mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
- if ((reg & AD1848_MCE) == 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return;
- }
-
- /*
- * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.
- * It may take up to 5 sample periods (at most 907 us @ 5.5125 kHz) for
- * the process to _start_, so it is important to wait at least that long
- * before checking. Otherwise we might think AC has finished when it
- * has in fact not begun. It could take 128 (no AC) or 384 (AC) cycles
- * for ACI to drop. This gives a wait of at most 70 ms with a more
- * typical value of 3-9 ms.
- */
- timeout = jiffies + msecs_to_jiffies(250);
- do {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- msleep(1);
- spin_lock_irqsave(&chip->reg_lock, flags);
- reg = snd_ad1848_in(chip, AD1848_TEST_INIT) &
- AD1848_CALIB_IN_PROGRESS;
- } while (reg && time_before(jiffies, timeout));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (reg)
- snd_printk(KERN_ERR
- "mce_down - auto calibration time out (2)\n");
-
- snd_printdd("(4) jiffies = %lu\n", jiffies);
- snd_printd("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL)));
-}
-
-static unsigned int snd_ad1848_get_count(unsigned char format,
- unsigned int size)
-{
- switch (format & 0xe0) {
- case AD1848_LINEAR_16:
- size >>= 1;
- break;
- }
- if (format & AD1848_STEREO)
- size >>= 1;
- return size;
-}
-
-static int snd_ad1848_trigger(struct snd_ad1848 *chip, unsigned char what,
- int channel, int cmd)
-{
- int result = 0;
-
-#if 0
- printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS)));
-#endif
- spin_lock(&chip->reg_lock);
- if (cmd == SNDRV_PCM_TRIGGER_START) {
- if (chip->image[AD1848_IFACE_CTRL] & what) {
- spin_unlock(&chip->reg_lock);
- return 0;
- }
- snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what);
- chip->mode |= AD1848_MODE_RUNNING;
- } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- if (!(chip->image[AD1848_IFACE_CTRL] & what)) {
- spin_unlock(&chip->reg_lock);
- return 0;
- }
- snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what);
- chip->mode &= ~AD1848_MODE_RUNNING;
- } else {
- result = -EINVAL;
- }
- spin_unlock(&chip->reg_lock);
- return result;
-}
-
-/*
- * CODEC I/O
- */
-
-static unsigned char snd_ad1848_get_rate(unsigned int rate)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (rate == rates[i])
- return freq_bits[i];
- snd_BUG();
- return freq_bits[ARRAY_SIZE(rates) - 1];
-}
-
-static int snd_ad1848_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
-static unsigned char snd_ad1848_get_format(int format, int channels)
-{
- unsigned char rformat;
-
- rformat = AD1848_LINEAR_8;
- switch (format) {
- case SNDRV_PCM_FORMAT_A_LAW: rformat = AD1848_ALAW_8; break;
- case SNDRV_PCM_FORMAT_MU_LAW: rformat = AD1848_ULAW_8; break;
- case SNDRV_PCM_FORMAT_S16_LE: rformat = AD1848_LINEAR_16; break;
- }
- if (channels > 1)
- rformat |= AD1848_STEREO;
-#if 0
- snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
-#endif
- return rformat;
-}
-
-static void snd_ad1848_calibrate_mute(struct snd_ad1848 *chip, int mute)
-{
- unsigned long flags;
-
- mute = mute ? 1 : 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->calibrate_mute == mute) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return;
- }
- if (!mute) {
- snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]);
- snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]);
- }
- snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]);
- snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]);
- snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]);
- snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]);
- snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]);
- snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]);
- chip->calibrate_mute = mute;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static void snd_ad1848_set_data_format(struct snd_ad1848 *chip, struct snd_pcm_hw_params *hw_params)
-{
- if (hw_params == NULL) {
- chip->image[AD1848_DATA_FORMAT] = 0x20;
- } else {
- chip->image[AD1848_DATA_FORMAT] =
- snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) |
- snd_ad1848_get_rate(params_rate(hw_params));
- }
- // snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]);
-}
-
-static int snd_ad1848_open(struct snd_ad1848 *chip, unsigned int mode)
-{
- unsigned long flags;
-
- if (chip->mode & AD1848_MODE_OPEN)
- return -EAGAIN;
-
- snd_ad1848_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("open: (1)\n");
-#endif
- snd_ad1848_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
- AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO |
- AD1848_CALIB_MODE);
- chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;
- snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_ad1848_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("open: (2)\n");
-#endif
-
- snd_ad1848_set_data_format(chip, NULL);
-
- snd_ad1848_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_ad1848_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("open: (3)\n");
-#endif
-
- /* ok. now enable and ack CODEC IRQ */
- spin_lock_irqsave(&chip->reg_lock, flags);
- outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
- outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
- chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE;
- snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- chip->mode = mode;
-
- return 0;
-}
-
-static void snd_ad1848_close(struct snd_ad1848 *chip)
-{
- unsigned long flags;
-
- if (!chip->mode)
- return;
- /* disable IRQ */
- spin_lock_irqsave(&chip->reg_lock, flags);
- outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
- outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
- chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE;
- snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- /* now disable capture & playback */
-
- snd_ad1848_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
- AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
- snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_ad1848_mce_down(chip);
-
- /* clear IRQ again */
- spin_lock_irqsave(&chip->reg_lock, flags);
- outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
- outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- chip->mode = 0;
-}
-
-/*
- * ok.. exported functions..
- */
-
-static int snd_ad1848_playback_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd);
-}
-
-static int snd_ad1848_capture_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd);
-}
-
-static int snd_ad1848_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- unsigned long flags;
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- snd_ad1848_calibrate_mute(chip, 1);
- snd_ad1848_set_data_format(chip, hw_params);
- snd_ad1848_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_ad1848_mce_down(chip);
- snd_ad1848_calibrate_mute(chip, 0);
- return 0;
-}
-
-static int snd_ad1848_playback_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_ad1848_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
- unsigned int count = snd_pcm_lib_period_bytes(substream);
-
- chip->dma_size = size;
- chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO);
- snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
- count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
- snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static int snd_ad1848_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- unsigned long flags;
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- snd_ad1848_calibrate_mute(chip, 1);
- snd_ad1848_set_data_format(chip, hw_params);
- snd_ad1848_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_ad1848_mce_down(chip);
- snd_ad1848_calibrate_mute(chip, 0);
- return 0;
-}
-
-static int snd_ad1848_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_ad1848_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
- unsigned int count = snd_pcm_lib_period_bytes(substream);
-
- chip->dma_size = size;
- chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
- snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
- count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
- snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id)
-{
- struct snd_ad1848 *chip = dev_id;
-
- if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream &&
- (chip->mode & AD1848_MODE_RUNNING))
- snd_pcm_period_elapsed(chip->playback_substream);
- if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream &&
- (chip->mode & AD1848_MODE_RUNNING))
- snd_pcm_period_elapsed(chip->capture_substream);
- outb(0, AD1848P(chip, STATUS)); /* clear global interrupt bit */
- return IRQ_HANDLED;
-}
-
-static snd_pcm_uframes_t snd_ad1848_playback_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- size_t ptr;
-
- if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE))
- return 0;
- ptr = snd_dma_pointer(chip->dma, chip->dma_size);
- return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_ad1848_capture_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- size_t ptr;
-
- if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE))
- return 0;
- ptr = snd_dma_pointer(chip->dma, chip->dma_size);
- return bytes_to_frames(substream->runtime, ptr);
-}
-
-/*
-
- */
-
-static void snd_ad1848_thinkpad_twiddle(struct snd_ad1848 *chip, int on) {
-
- int tmp;
-
- if (!chip->thinkpad_flag) return;
-
- outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
- tmp = inb(AD1848_THINKPAD_CTL_PORT2);
-
- if (on)
- /* turn it on */
- tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
- else
- /* turn it off */
- tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
-
- outb(tmp, AD1848_THINKPAD_CTL_PORT2);
-
-}
-
-#ifdef CONFIG_PM
-static void snd_ad1848_suspend(struct snd_ad1848 *chip)
-{
- snd_pcm_suspend_all(chip->pcm);
- if (chip->thinkpad_flag)
- snd_ad1848_thinkpad_twiddle(chip, 0);
-}
-
-static void snd_ad1848_resume(struct snd_ad1848 *chip)
-{
- int i;
-
- if (chip->thinkpad_flag)
- snd_ad1848_thinkpad_twiddle(chip, 1);
-
- /* clear any pendings IRQ */
- inb(AD1848P(chip, STATUS));
- outb(0, AD1848P(chip, STATUS));
- mb();
-
- snd_ad1848_mce_down(chip);
- for (i = 0; i < 16; i++)
- snd_ad1848_out(chip, i, chip->image[i]);
- snd_ad1848_mce_up(chip);
- snd_ad1848_mce_down(chip);
-}
-#endif /* CONFIG_PM */
-
-static int snd_ad1848_probe(struct snd_ad1848 * chip)
-{
- unsigned long flags;
- int i, id, rev, ad1847;
- unsigned char *ptr;
-
-#if 0
- snd_ad1848_debug(chip);
-#endif
- id = ad1847 = 0;
- for (i = 0; i < 1000; i++) {
- mb();
- if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
- udelay(500);
- else {
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
- snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa);
- snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45);
- rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT);
- if (rev == 0x65) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- id = 1;
- ad1847 = 1;
- break;
- }
- if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- id = 1;
- break;
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- }
- }
- if (id != 1)
- return -ENODEV; /* no valid device found */
- if (chip->hardware == AD1848_HW_DETECT) {
- if (ad1847) {
- chip->hardware = AD1848_HW_AD1847;
- } else {
- chip->hardware = AD1848_HW_AD1848;
- rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
- if (rev & 0x80) {
- chip->hardware = AD1848_HW_CS4248;
- } else if ((rev & 0x0f) == 0x0a) {
- snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40);
- for (i = 0; i < 16; ++i) {
- if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) {
- chip->hardware = AD1848_HW_CMI8330;
- break;
- }
- }
- snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
- }
- }
- }
- spin_lock_irqsave(&chip->reg_lock, flags);
- inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */
- outb(0, AD1848P(chip, STATUS));
- mb();
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- chip->image[AD1848_MISC_INFO] = 0x00;
- chip->image[AD1848_IFACE_CTRL] =
- (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA;
- ptr = (unsigned char *) &chip->image;
- snd_ad1848_mce_down(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */
- snd_ad1848_out(chip, i, *ptr++);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_ad1848_mce_up(chip);
- snd_ad1848_mce_down(chip);
- return 0; /* all things are ok.. */
-}
-
-/*
-
- */
-
-static struct snd_pcm_hardware snd_ad1848_playback =
-{
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
- .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5510,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_ad1848_capture =
-{
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
- .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5510,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
-/*
-
- */
-
-static int snd_ad1848_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0)
- return err;
- chip->playback_substream = substream;
- runtime->hw = snd_ad1848_playback;
- snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
- snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- return 0;
-}
-
-static int snd_ad1848_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0)
- return err;
- chip->capture_substream = substream;
- runtime->hw = snd_ad1848_capture;
- snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
- snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- return 0;
-}
-
-static int snd_ad1848_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-
- chip->mode &= ~AD1848_MODE_PLAY;
- chip->playback_substream = NULL;
- snd_ad1848_close(chip);
- return 0;
-}
-
-static int snd_ad1848_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-
- chip->mode &= ~AD1848_MODE_CAPTURE;
- chip->capture_substream = NULL;
- snd_ad1848_close(chip);
- return 0;
-}
-
-static int snd_ad1848_free(struct snd_ad1848 *chip)
-{
- release_and_free_resource(chip->res_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
- if (chip->dma >= 0) {
- snd_dma_disable(chip->dma);
- free_dma(chip->dma);
- }
- kfree(chip);
- return 0;
-}
-
-static int snd_ad1848_dev_free(struct snd_device *device)
-{
- struct snd_ad1848 *chip = device->device_data;
- return snd_ad1848_free(chip);
-}
-
-static const char *snd_ad1848_chip_id(struct snd_ad1848 *chip)
-{
- switch (chip->hardware) {
- case AD1848_HW_AD1847: return "AD1847";
- case AD1848_HW_AD1848: return "AD1848";
- case AD1848_HW_CS4248: return "CS4248";
- case AD1848_HW_CMI8330: return "CMI8330/C3D";
- default: return "???";
- }
-}
-
-int snd_ad1848_create(struct snd_card *card,
- unsigned long port,
- int irq, int dma,
- unsigned short hardware,
- struct snd_ad1848 ** rchip)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_ad1848_dev_free,
- };
- struct snd_ad1848 *chip;
- int err;
-
- *rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
- spin_lock_init(&chip->reg_lock);
- chip->card = card;
- chip->port = port;
- chip->irq = -1;
- chip->dma = -1;
- chip->hardware = hardware;
- memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image));
-
- if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) {
- snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port);
- snd_ad1848_free(chip);
- return -EBUSY;
- }
- if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) {
- snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq);
- snd_ad1848_free(chip);
- return -EBUSY;
- }
- chip->irq = irq;
- if (request_dma(dma, "AD1848")) {
- snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma);
- snd_ad1848_free(chip);
- return -EBUSY;
- }
- chip->dma = dma;
-
- if (hardware == AD1848_HW_THINKPAD) {
- chip->thinkpad_flag = 1;
- chip->hardware = AD1848_HW_DETECT; /* reset */
- snd_ad1848_thinkpad_twiddle(chip, 1);
- }
-
- if (snd_ad1848_probe(chip) < 0) {
- snd_ad1848_free(chip);
- return -ENODEV;
- }
-
- /* Register device */
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ad1848_free(chip);
- return err;
- }
-
-#ifdef CONFIG_PM
- chip->suspend = snd_ad1848_suspend;
- chip->resume = snd_ad1848_resume;
-#endif
-
- *rchip = chip;
- return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_create);
-
-static struct snd_pcm_ops snd_ad1848_playback_ops = {
- .open = snd_ad1848_playback_open,
- .close = snd_ad1848_playback_close,
- .ioctl = snd_ad1848_ioctl,
- .hw_params = snd_ad1848_playback_hw_params,
- .hw_free = snd_ad1848_playback_hw_free,
- .prepare = snd_ad1848_playback_prepare,
- .trigger = snd_ad1848_playback_trigger,
- .pointer = snd_ad1848_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_ad1848_capture_ops = {
- .open = snd_ad1848_capture_open,
- .close = snd_ad1848_capture_close,
- .ioctl = snd_ad1848_ioctl,
- .hw_params = snd_ad1848_capture_hw_params,
- .hw_free = snd_ad1848_capture_hw_free,
- .prepare = snd_ad1848_capture_prepare,
- .trigger = snd_ad1848_capture_trigger,
- .pointer = snd_ad1848_capture_pointer,
-};
-
-int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm)
-{
- struct snd_pcm *pcm;
- int err;
-
- if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0)
- return err;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops);
-
- pcm->private_data = chip;
- pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- strcpy(pcm->name, snd_ad1848_chip_id(chip));
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, chip->dma > 3 ? 128*1024 : 64*1024);
-
- chip->pcm = pcm;
- if (rpcm)
- *rpcm = pcm;
- return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_pcm);
-
-const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction)
-{
- return direction == SNDRV_PCM_STREAM_PLAYBACK ?
- &snd_ad1848_playback_ops : &snd_ad1848_capture_ops;
-}
-
-EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
-
-/*
- * MIXER part
- */
-
-static int snd_ad1848_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[4] = {
- "Line", "Aux", "Mic", "Mix"
- };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int snd_ad1848_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6;
- ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static int snd_ad1848_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- unsigned short left, right;
- int change;
-
- if (ucontrol->value.enumerated.item[0] > 3 ||
- ucontrol->value.enumerated.item[1] > 3)
- return -EINVAL;
- left = ucontrol->value.enumerated.item[0] << 6;
- right = ucontrol->value.enumerated.item[1] << 6;
- spin_lock_irqsave(&chip->reg_lock, flags);
- left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left;
- right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right;
- change = left != chip->image[AD1848_LEFT_INPUT] ||
- right != chip->image[AD1848_RIGHT_INPUT];
- snd_ad1848_out(chip, AD1848_LEFT_INPUT, left);
- snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return change;
-}
-
-static int snd_ad1848_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-static int snd_ad1848_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- return 0;
-}
-
-static int snd_ad1848_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
- int change;
- unsigned short val;
-
- val = (ucontrol->value.integer.value[0] & mask);
- if (invert)
- val = mask - val;
- val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
- val = (chip->image[reg] & ~(mask << shift)) | val;
- change = val != chip->image[reg];
- snd_ad1848_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return change;
-}
-
-static int snd_ad1848_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 24) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-static int snd_ad1848_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int left_reg = kcontrol->private_value & 0xff;
- int right_reg = (kcontrol->private_value >> 8) & 0xff;
- int shift_left = (kcontrol->private_value >> 16) & 0x07;
- int shift_right = (kcontrol->private_value >> 19) & 0x07;
- int mask = (kcontrol->private_value >> 24) & 0xff;
- int invert = (kcontrol->private_value >> 22) & 1;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
- ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (invert) {
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
- }
- return 0;
-}
-
-static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int left_reg = kcontrol->private_value & 0xff;
- int right_reg = (kcontrol->private_value >> 8) & 0xff;
- int shift_left = (kcontrol->private_value >> 16) & 0x07;
- int shift_right = (kcontrol->private_value >> 19) & 0x07;
- int mask = (kcontrol->private_value >> 24) & 0xff;
- int invert = (kcontrol->private_value >> 22) & 1;
- int change;
- unsigned short val1, val2;
-
- val1 = ucontrol->value.integer.value[0] & mask;
- val2 = ucontrol->value.integer.value[1] & mask;
- if (invert) {
- val1 = mask - val1;
- val2 = mask - val2;
- }
- val1 <<= shift_left;
- val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (left_reg != right_reg) {
- val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
- val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
- change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
- snd_ad1848_out(chip, left_reg, val1);
- snd_ad1848_out(chip, right_reg, val2);
- } else {
- val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
- change = val1 != chip->image[left_reg];
- snd_ad1848_out(chip, left_reg, val1);
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return change;
-}
-
-/*
- */
-int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
- const struct ad1848_mix_elem *c)
-{
- static struct snd_kcontrol_new newctls[] = {
- [AD1848_MIX_SINGLE] = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = snd_ad1848_info_single,
- .get = snd_ad1848_get_single,
- .put = snd_ad1848_put_single,
- },
- [AD1848_MIX_DOUBLE] = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = snd_ad1848_info_double,
- .get = snd_ad1848_get_double,
- .put = snd_ad1848_put_double,
- },
- [AD1848_MIX_CAPTURE] = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = snd_ad1848_info_mux,
- .get = snd_ad1848_get_mux,
- .put = snd_ad1848_put_mux,
- },
- };
- struct snd_kcontrol *ctl;
- int err;
-
- ctl = snd_ctl_new1(&newctls[c->type], chip);
- if (! ctl)
- return -ENOMEM;
- strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name));
- ctl->id.index = c->index;
- ctl->private_value = c->private_value;
- if (c->tlv) {
- ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- ctl->tlv.p = c->tlv;
- }
- if ((err = snd_ctl_add(chip->card, ctl)) < 0)
- return err;
- return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
-
-static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-
-static struct ad1848_mix_elem snd_ad1848_controls[] = {
-AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1,
- db_scale_6bit),
-AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
- db_scale_5bit_12db_max),
-AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
- db_scale_5bit_12db_max),
-AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0,
- db_scale_rec_gain),
-{
- .name = "Capture Source",
- .type = AD1848_MIX_CAPTURE,
-},
-AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
-AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0,
- db_scale_6bit),
-};
-
-int snd_ad1848_mixer(struct snd_ad1848 *chip)
-{
- struct snd_card *card;
- struct snd_pcm *pcm;
- unsigned int idx;
- int err;
-
- snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
-
- pcm = chip->pcm;
- card = chip->card;
-
- strcpy(card->mixername, pcm->name);
-
- for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++)
- if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0)
- return err;
-
- return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_mixer);
-
-/*
- * INIT part
- */
-
-static int __init alsa_ad1848_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ad1848_exit(void)
-{
-}
-
-module_init(alsa_ad1848_init)
-module_exit(alsa_ad1848_exit)
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index 154e728f592..3e74d1a3928 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -38,7 +38,7 @@
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
@@ -76,7 +76,7 @@ struct snd_card_azt2320 {
int dev_no;
struct pnp_dev *dev;
struct pnp_dev *devmpu;
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
};
static struct pnp_card_device_id snd_azt2320_pnpids[] = {
@@ -181,7 +181,7 @@ static int __devinit snd_card_azt2320_probe(int dev,
int error;
struct snd_card *card;
struct snd_card_azt2320 *acard;
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
struct snd_opl3 *opl3;
if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
@@ -200,11 +200,11 @@ static int __devinit snd_card_azt2320_probe(int dev,
return error;
}
- if ((error = snd_cs4231_create(card, wss_port[dev], -1,
- irq[dev],
- dma1[dev],
- dma2[dev],
- CS4231_HW_DETECT, 0, &chip)) < 0) {
+ error = snd_wss_create(card, wss_port[dev], -1,
+ irq[dev],
+ dma1[dev], dma2[dev],
+ WSS_HW_DETECT, 0, &chip);
+ if (error < 0) {
snd_card_free(card);
return error;
}
@@ -214,15 +214,18 @@ static int __devinit snd_card_azt2320_probe(int dev,
sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
- if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
+ error = snd_wss_pcm(chip, 0, NULL);
+ if (error < 0) {
snd_card_free(card);
return error;
}
- if ((error = snd_cs4231_mixer(chip)) < 0) {
+ error = snd_wss_mixer(chip);
+ if (error < 0) {
snd_card_free(card);
return error;
}
- if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+ error = snd_wss_timer(chip, 0, NULL);
+ if (error < 0) {
snd_card_free(card);
return error;
}
@@ -293,7 +296,7 @@ static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t sta
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
struct snd_card_azt2320 *acard = card->private_data;
- struct snd_cs4231 *chip = acard->chip;
+ struct snd_wss *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->suspend(chip);
@@ -304,7 +307,7 @@ static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
struct snd_card_azt2320 *acard = card->private_data;
- struct snd_cs4231 *chip = acard->chip;
+ struct snd_wss *chip = acard->chip;
chip->resume(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index 4d198ec71e9..e49aec700a5 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -50,7 +50,7 @@
#include <linux/pnp.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
#include <sound/sb.h>
#include <sound/initval.h>
@@ -151,7 +151,7 @@ struct snd_cmi8330 {
struct pnp_dev *play;
#endif
struct snd_card *card;
- struct snd_ad1848 *wss;
+ struct snd_wss *wss;
struct snd_sb *sb;
struct snd_pcm *pcm;
@@ -174,32 +174,57 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids);
#endif
-static struct ad1848_mix_elem snd_cmi8330_controls[] __devinitdata = {
-AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
-AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1),
-AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
-AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
-AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
-AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
-AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0),
-AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0),
-AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0),
-AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0),
-AD1848_DOUBLE("CD Capture Volume", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0),
-AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0),
-AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0),
-AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0),
-AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0),
-AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0),
-AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0),
-AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0),
-AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
-AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1),
-AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0),
-AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1),
-AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",CAPTURE,SWITCH), 0, CMI8330_RMUX3D, 7, 1, 1),
-AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",PLAYBACK,SWITCH), 0, CMI8330_MUTEMUX, 7, 1, 1),
+static struct snd_kcontrol_new snd_cmi8330_controls[] __devinitdata = {
+WSS_DOUBLE("Master Playback Volume", 0,
+ CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
+WSS_SINGLE("Loud Playback Switch", 0,
+ CMI8330_MUTEMUX, 6, 1, 1),
+WSS_DOUBLE("PCM Playback Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+ CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
+WSS_DOUBLE("Line Playback Volume", 0,
+ CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
+WSS_DOUBLE("Line Capture Switch", 0,
+ CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
+WSS_DOUBLE("Line Capture Volume", 0,
+ CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0),
+WSS_DOUBLE("CD Playback Switch", 0,
+ CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0),
+WSS_DOUBLE("CD Capture Switch", 0,
+ CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0),
+WSS_DOUBLE("CD Playback Volume", 0,
+ CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0),
+WSS_DOUBLE("CD Capture Volume", 0,
+ CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0),
+WSS_SINGLE("Mic Playback Switch", 0,
+ CMI8330_MUTEMUX, 0, 1, 0),
+WSS_SINGLE("Mic Playback Volume", 0,
+ CMI8330_OUTPUTVOL, 0, 7, 0),
+WSS_SINGLE("Mic Capture Switch", 0,
+ CMI8330_RMUX3D, 0, 1, 0),
+WSS_SINGLE("Mic Capture Volume", 0,
+ CMI8330_OUTPUTVOL, 5, 7, 0),
+WSS_DOUBLE("Wavetable Playback Switch", 0,
+ CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0),
+WSS_DOUBLE("Wavetable Playback Volume", 0,
+ CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0),
+WSS_DOUBLE("Wavetable Capture Switch", 0,
+ CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0),
+WSS_DOUBLE("Wavetable Capture Volume", 0,
+ CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
+WSS_SINGLE("3D Control - Switch", 0,
+ CMI8330_RMUX3D, 5, 1, 1),
+WSS_SINGLE("PC Speaker Playback Volume", 0,
+ CMI8330_OUTPUTVOL, 3, 3, 0),
+WSS_SINGLE("FM Playback Switch", 0,
+ CMI8330_RECMUX, 3, 1, 1),
+WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0,
+ CMI8330_RMUX3D, 7, 1, 1),
+WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0,
+ CMI8330_MUTEMUX, 7, 1, 1),
};
#ifdef ENABLE_SB_MIXER
@@ -268,7 +293,10 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330
strcpy(card->mixername, "CMI8330/C3D");
for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
- if ((err = snd_ad1848_add_ctl_elem(acard->wss, &snd_cmi8330_controls[idx])) < 0)
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_cmi8330_controls[idx],
+ acard->wss));
+ if (err < 0)
return err;
}
@@ -385,7 +413,7 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *
chip->streams[CMI_SB_STREAM].private_data = chip->sb;
/* AD1848 */
- ops = snd_ad1848_get_pcm_ops(CMI_AD_STREAM);
+ ops = snd_wss_get_pcm_ops(CMI_AD_STREAM);
chip->streams[CMI_AD_STREAM].ops = *ops;
chip->streams[CMI_AD_STREAM].open = ops->open;
chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM];
@@ -461,16 +489,15 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
int i, err;
acard = card->private_data;
- if ((err = snd_ad1848_create(card,
- wssport[dev] + 4,
- wssirq[dev],
- wssdma[dev],
- AD1848_HW_DETECT,
- &acard->wss)) < 0) {
+ err = snd_wss_create(card, wssport[dev] + 4, -1,
+ wssirq[dev],
+ wssdma[dev], -1,
+ WSS_HW_DETECT, 0, &acard->wss);
+ if (err < 0) {
snd_printk(KERN_ERR PFX "(AD1848) device busy??\n");
return err;
}
- if (acard->wss->hardware != AD1848_HW_CMI8330) {
+ if (acard->wss->hardware != WSS_HW_CMI8330) {
snd_printk(KERN_ERR PFX "(AD1848) not found during probe\n");
return -ENODEV;
}
@@ -489,9 +516,10 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
return err;
}
- snd_ad1848_out(acard->wss, AD1848_MISC_INFO, 0x40); /* switch on MODE2 */
+ snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */
for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++)
- snd_ad1848_out(acard->wss, i, snd_cmi8330_image[i - CMI8330_RMUX3D]);
+ snd_wss_out(acard->wss, i,
+ snd_cmi8330_image[i - CMI8330_RMUX3D]);
if ((err = snd_cmi8330_mixer(card, acard)) < 0) {
snd_printk(KERN_ERR PFX "failed to create mixers\n");
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index 5067ee00193..5870ca21ab5 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -3,14 +3,12 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-cs4231-lib-objs := cs4231_lib.o
snd-cs4236-lib-objs := cs4236_lib.o
snd-cs4231-objs := cs4231.o
snd-cs4232-objs := cs4232.o
snd-cs4236-objs := cs4236.o
# Toplevel Module Dependency
-obj-$(CONFIG_SND_CS4231_LIB) += snd-cs4231-lib.o
obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
obj-$(CONFIG_SND_CS4232) += snd-cs4232.o
obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index e9462b9944b..ddd289120aa 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -27,7 +27,7 @@
#include <linux/wait.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
@@ -91,7 +91,7 @@ static int __devinit snd_cs4231_match(struct device *dev, unsigned int n)
static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
struct snd_pcm *pcm;
int error;
@@ -99,14 +99,14 @@ static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
if (!card)
return -EINVAL;
- error = snd_cs4231_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
- CS4231_HW_DETECT, 0, &chip);
+ error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
+ WSS_HW_DETECT, 0, &chip);
if (error < 0)
goto out;
card->private_data = chip;
- error = snd_cs4231_pcm(chip, 0, &pcm);
+ error = snd_wss_pcm(chip, 0, &pcm);
if (error < 0)
goto out;
@@ -118,11 +118,11 @@ static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
if (dma2[n] >= 0)
sprintf(card->longname + strlen(card->longname), "&%d", dma2[n]);
- error = snd_cs4231_mixer(chip);
+ error = snd_wss_mixer(chip);
if (error < 0)
goto out;
- error = snd_cs4231_timer(chip, 0, NULL);
+ error = snd_wss_timer(chip, 0, NULL);
if (error < 0)
goto out;
@@ -160,7 +160,7 @@ static int __devexit snd_cs4231_remove(struct device *dev, unsigned int n)
static int snd_cs4231_suspend(struct device *dev, unsigned int n, pm_message_t state)
{
struct snd_card *card = dev_get_drvdata(dev);
- struct snd_cs4231 *chip = card->private_data;
+ struct snd_wss *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->suspend(chip);
@@ -170,7 +170,7 @@ static int snd_cs4231_suspend(struct device *dev, unsigned int n, pm_message_t s
static int snd_cs4231_resume(struct device *dev, unsigned int n)
{
struct snd_card *card = dev_get_drvdata(dev);
- struct snd_cs4231 *chip = card->private_data;
+ struct snd_wss *chip = card->private_data;
chip->resume(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
deleted file mode 100644
index 0aa8649e5c7..00000000000
--- a/sound/isa/cs423x/cs4231_lib.c
+++ /dev/null
@@ -1,1845 +0,0 @@
-/*
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
- *
- * Bugs:
- * - sometimes record brokes playback with WSS portion of
- * Yamaha OPL3-SA3 chip
- * - CS4231 (GUS MAX) - still trouble with occasional noises
- * - broken initialization?
- *
- * 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/delay.h>
-#include <linux/pm.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <sound/core.h>
-#include <sound/cs4231.h>
-#include <sound/pcm_params.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
-MODULE_LICENSE("GPL");
-
-#if 0
-#define SNDRV_DEBUG_MCE
-#endif
-
-/*
- * Some variables
- */
-
-static unsigned char freq_bits[14] = {
- /* 5510 */ 0x00 | CS4231_XTAL2,
- /* 6620 */ 0x0E | CS4231_XTAL2,
- /* 8000 */ 0x00 | CS4231_XTAL1,
- /* 9600 */ 0x0E | CS4231_XTAL1,
- /* 11025 */ 0x02 | CS4231_XTAL2,
- /* 16000 */ 0x02 | CS4231_XTAL1,
- /* 18900 */ 0x04 | CS4231_XTAL2,
- /* 22050 */ 0x06 | CS4231_XTAL2,
- /* 27042 */ 0x04 | CS4231_XTAL1,
- /* 32000 */ 0x06 | CS4231_XTAL1,
- /* 33075 */ 0x0C | CS4231_XTAL2,
- /* 37800 */ 0x08 | CS4231_XTAL2,
- /* 44100 */ 0x0A | CS4231_XTAL2,
- /* 48000 */ 0x0C | CS4231_XTAL1
-};
-
-static unsigned int rates[14] = {
- 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
- 27042, 32000, 33075, 37800, 44100, 48000
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static int snd_cs4231_xrate(struct snd_pcm_runtime *runtime)
-{
- return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-}
-
-static unsigned char snd_cs4231_original_image[32] =
-{
- 0x00, /* 00/00 - lic */
- 0x00, /* 01/01 - ric */
- 0x9f, /* 02/02 - la1ic */
- 0x9f, /* 03/03 - ra1ic */
- 0x9f, /* 04/04 - la2ic */
- 0x9f, /* 05/05 - ra2ic */
- 0xbf, /* 06/06 - loc */
- 0xbf, /* 07/07 - roc */
- 0x20, /* 08/08 - pdfr */
- CS4231_AUTOCALIB, /* 09/09 - ic */
- 0x00, /* 0a/10 - pc */
- 0x00, /* 0b/11 - ti */
- CS4231_MODE2, /* 0c/12 - mi */
- 0xfc, /* 0d/13 - lbc */
- 0x00, /* 0e/14 - pbru */
- 0x00, /* 0f/15 - pbrl */
- 0x80, /* 10/16 - afei */
- 0x01, /* 11/17 - afeii */
- 0x9f, /* 12/18 - llic */
- 0x9f, /* 13/19 - rlic */
- 0x00, /* 14/20 - tlb */
- 0x00, /* 15/21 - thb */
- 0x00, /* 16/22 - la3mic/reserved */
- 0x00, /* 17/23 - ra3mic/reserved */
- 0x00, /* 18/24 - afs */
- 0x00, /* 19/25 - lamoc/version */
- 0xcf, /* 1a/26 - mioc */
- 0x00, /* 1b/27 - ramoc/reserved */
- 0x20, /* 1c/28 - cdfr */
- 0x00, /* 1d/29 - res4 */
- 0x00, /* 1e/30 - cbru */
- 0x00, /* 1f/31 - cbrl */
-};
-
-/*
- * Basic I/O functions
- */
-
-static inline void cs4231_outb(struct snd_cs4231 *chip, u8 offset, u8 val)
-{
- outb(val, chip->port + offset);
-}
-
-static inline u8 cs4231_inb(struct snd_cs4231 *chip, u8 offset)
-{
- return inb(chip->port + offset);
-}
-
-static void snd_cs4231_wait(struct snd_cs4231 *chip)
-{
- int timeout;
-
- for (timeout = 250;
- timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
- timeout--)
- udelay(100);
-}
-
-static void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg,
- unsigned char mask, unsigned char value)
-{
- unsigned char tmp = (chip->image[reg] & mask) | value;
-
- snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
- chip->image[reg] = tmp;
- if (!chip->calibrate_mute) {
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
- wmb();
- cs4231_outb(chip, CS4231P(REG), tmp);
- mb();
- }
-}
-
-static void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg, unsigned char value)
-{
- int timeout;
-
- for (timeout = 250;
- timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
- timeout--)
- udelay(10);
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
- cs4231_outb(chip, CS4231P(REG), value);
- mb();
-}
-
-void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char value)
-{
- snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
- cs4231_outb(chip, CS4231P(REG), value);
- chip->image[reg] = value;
- mb();
- snd_printdd("codec out - reg 0x%x = 0x%x\n",
- chip->mce_bit | reg, value);
-}
-
-unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg)
-{
- snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
-#endif
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
- mb();
- return cs4231_inb(chip, CS4231P(REG));
-}
-
-void snd_cs4236_ext_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val)
-{
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
- cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01));
- cs4231_outb(chip, CS4231P(REG), val);
- chip->eimage[CS4236_REG(reg)] = val;
-#if 0
- printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
-#endif
-}
-
-unsigned char snd_cs4236_ext_in(struct snd_cs4231 *chip, unsigned char reg)
-{
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
- cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01));
-#if 1
- return cs4231_inb(chip, CS4231P(REG));
-#else
- {
- unsigned char res;
- res = cs4231_inb(chip, CS4231P(REG));
- printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
- return res;
- }
-#endif
-}
-
-#if 0
-
-static void snd_cs4231_debug(struct snd_cs4231 *chip)
-{
- printk("CS4231 REGS: INDEX = 0x%02x ", cs4231_inb(chip, CS4231P(REGSEL)));
- printk(" STATUS = 0x%02x\n", cs4231_inb(chip, CS4231P(STATUS)));
- printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00));
- printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10));
- printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01));
- printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11));
- printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02));
- printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12));
- printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03));
- printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13));
- printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04));
- printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14));
- printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05));
- printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15));
- printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06));
- printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16));
- printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07));
- printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17));
- printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08));
- printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18));
- printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09));
- printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19));
- printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a));
- printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a));
- printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b));
- printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b));
- printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c));
- printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c));
- printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d));
- printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d));
- printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e));
- printk(" 0x1e: ply lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e));
- printk(" 0x0f: rec upr count = 0x%02x ", snd_cs4231_in(chip, 0x0f));
- printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f));
-}
-
-#endif
-
-/*
- * CS4231 detection / MCE routines
- */
-
-static void snd_cs4231_busy_wait(struct snd_cs4231 *chip)
-{
- int timeout;
-
- /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
- for (timeout = 5; timeout > 0; timeout--)
- cs4231_inb(chip, CS4231P(REGSEL));
- /* end of cleanup sequence */
- for (timeout = 250;
- timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
- timeout--)
- udelay(10);
-}
-
-void snd_cs4231_mce_up(struct snd_cs4231 *chip)
-{
- unsigned long flags;
- int timeout;
-
- snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("mce_up - auto calibration time out (0)\n");
-#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit |= CS4231_MCE;
- timeout = cs4231_inb(chip, CS4231P(REGSEL));
- if (timeout == 0x80)
- snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
- if (!(timeout & CS4231_MCE))
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-void snd_cs4231_mce_down(struct snd_cs4231 *chip)
-{
- unsigned long flags;
- unsigned long end_time;
- int timeout;
-
- snd_cs4231_busy_wait(chip);
-
-#ifdef CONFIG_SND_DEBUG
- if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL));
-#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit &= ~CS4231_MCE;
- timeout = cs4231_inb(chip, CS4231P(REGSEL));
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (timeout == 0x80)
- snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
- if ((timeout & CS4231_MCE) == 0 ||
- !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
- return;
- }
-
- /*
- * Wait for (possible -- during init auto-calibration may not be set)
- * calibration process to start. Needs upto 5 sample periods on AD1848
- * which at the slowest possible rate of 5.5125 kHz means 907 us.
- */
- msleep(1);
-
- snd_printdd("(1) jiffies = %lu\n", jiffies);
-
- /* check condition up to 250 ms */
- end_time = jiffies + msecs_to_jiffies(250);
- while (snd_cs4231_in(chip, CS4231_TEST_INIT) &
- CS4231_CALIB_IN_PROGRESS) {
-
- if (time_after(jiffies, end_time)) {
- snd_printk(KERN_ERR "mce_down - "
- "auto calibration time out (2)\n");
- return;
- }
- msleep(1);
- }
-
- snd_printdd("(2) jiffies = %lu\n", jiffies);
-
- /* check condition up to 100 ms */
- end_time = jiffies + msecs_to_jiffies(100);
- while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
- if (time_after(jiffies, end_time)) {
- snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
- return;
- }
- msleep(1);
- }
-
- snd_printdd("(3) jiffies = %lu\n", jiffies);
- snd_printd("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL)));
-}
-
-static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size)
-{
- switch (format & 0xe0) {
- case CS4231_LINEAR_16:
- case CS4231_LINEAR_16_BIG:
- size >>= 1;
- break;
- case CS4231_ADPCM_16:
- return size >> 2;
- }
- if (format & CS4231_STEREO)
- size >>= 1;
- return size;
-}
-
-static int snd_cs4231_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- int result = 0;
- unsigned int what;
- struct snd_pcm_substream *s;
- int do_start;
-
-#if 0
- printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, cs4231_inb(chip, CS4231P(STATUS)));
-#endif
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- do_start = 1; break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- do_start = 0; break;
- default:
- return -EINVAL;
- }
-
- what = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s == chip->playback_substream) {
- what |= CS4231_PLAYBACK_ENABLE;
- snd_pcm_trigger_done(s, substream);
- } else if (s == chip->capture_substream) {
- what |= CS4231_RECORD_ENABLE;
- snd_pcm_trigger_done(s, substream);
- }
- }
- spin_lock(&chip->reg_lock);
- if (do_start) {
- chip->image[CS4231_IFACE_CTRL] |= what;
- if (chip->trigger)
- chip->trigger(chip, what, 1);
- } else {
- chip->image[CS4231_IFACE_CTRL] &= ~what;
- if (chip->trigger)
- chip->trigger(chip, what, 0);
- }
- snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock(&chip->reg_lock);
-#if 0
- snd_cs4231_debug(chip);
-#endif
- return result;
-}
-
-/*
- * CODEC I/O
- */
-
-static unsigned char snd_cs4231_get_rate(unsigned int rate)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (rate == rates[i])
- return freq_bits[i];
- // snd_BUG();
- return freq_bits[ARRAY_SIZE(rates) - 1];
-}
-
-static unsigned char snd_cs4231_get_format(struct snd_cs4231 *chip,
- int format,
- int channels)
-{
- unsigned char rformat;
-
- rformat = CS4231_LINEAR_8;
- switch (format) {
- case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break;
- case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break;
- case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break;
- case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break;
- case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break;
- }
- if (channels > 1)
- rformat |= CS4231_STEREO;
-#if 0
- snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
-#endif
- return rformat;
-}
-
-static void snd_cs4231_calibrate_mute(struct snd_cs4231 *chip, int mute)
-{
- unsigned long flags;
-
- mute = mute ? 1 : 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->calibrate_mute == mute) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return;
- }
- if (!mute) {
- snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]);
- snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]);
- snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]);
- }
- snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
- snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
- snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
- snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
- snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
- snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
- snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
- snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
- snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
- if (chip->hardware == CS4231_HW_INTERWAVE) {
- snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]);
- snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]);
- snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]);
- snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]);
- }
- chip->calibrate_mute = mute;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static void snd_cs4231_playback_format(struct snd_cs4231 *chip,
- struct snd_pcm_hw_params *params,
- unsigned char pdfr)
-{
- unsigned long flags;
- int full_calib = 1;
-
- mutex_lock(&chip->mce_mutex);
- snd_cs4231_calibrate_mute(chip, 1);
- if (chip->hardware == CS4231_HW_CS4231A ||
- (chip->hardware & CS4231_HW_CS4232_MASK)) {
- spin_lock_irqsave(&chip->reg_lock, flags);
- if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
- udelay(100); /* Fixes audible clicks at least on GUS MAX */
- full_calib = 0;
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- }
- if (full_calib) {
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) {
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
- (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
- (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
- pdfr);
- } else {
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (chip->hardware == CS4231_HW_OPL3SA2)
- udelay(100); /* this seems to help */
- snd_cs4231_mce_down(chip);
- }
- snd_cs4231_calibrate_mute(chip, 0);
- mutex_unlock(&chip->mce_mutex);
-}
-
-static void snd_cs4231_capture_format(struct snd_cs4231 *chip,
- struct snd_pcm_hw_params *params,
- unsigned char cdfr)
-{
- unsigned long flags;
- int full_calib = 1;
-
- mutex_lock(&chip->mce_mutex);
- snd_cs4231_calibrate_mute(chip, 1);
- if (chip->hardware == CS4231_HW_CS4231A ||
- (chip->hardware & CS4231_HW_CS4232_MASK)) {
- spin_lock_irqsave(&chip->reg_lock, flags);
- if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */
- (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
- snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
- full_calib = 0;
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- }
- if (full_calib) {
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->hardware != CS4231_HW_INTERWAVE) {
- if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
- ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
- (cdfr & 0x0f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- }
- }
- snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
- }
- snd_cs4231_calibrate_mute(chip, 0);
- mutex_unlock(&chip->mce_mutex);
-}
-
-/*
- * Timer interface
- */
-
-static unsigned long snd_cs4231_timer_resolution(struct snd_timer * timer)
-{
- struct snd_cs4231 *chip = snd_timer_chip(timer);
- if (chip->hardware & CS4231_HW_CS4236B_MASK)
- return 14467;
- else
- return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
-}
-
-static int snd_cs4231_timer_start(struct snd_timer * timer)
-{
- unsigned long flags;
- unsigned int ticks;
- struct snd_cs4231 *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
- ticks = timer->sticks;
- if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
- (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
- (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
- snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8));
- snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE);
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static int snd_cs4231_timer_stop(struct snd_timer * timer)
-{
- unsigned long flags;
- struct snd_cs4231 *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static void snd_cs4231_init(struct snd_cs4231 *chip)
-{
- unsigned long flags;
-
- snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (1)\n");
-#endif
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
- CS4231_RECORD_ENABLE | CS4231_RECORD_PIO |
- CS4231_CALIB_MODE);
- chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
- snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (2)\n");
-#endif
-
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]);
-#endif
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (4)\n");
-#endif
-
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (5)\n");
-#endif
-}
-
-static int snd_cs4231_open(struct snd_cs4231 *chip, unsigned int mode)
-{
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
- if ((chip->mode & mode) ||
- ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) {
- mutex_unlock(&chip->open_mutex);
- return -EAGAIN;
- }
- if (chip->mode & CS4231_MODE_OPEN) {
- chip->mode |= mode;
- mutex_unlock(&chip->open_mutex);
- return 0;
- }
- /* ok. now enable and ack CODEC IRQ */
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
- CS4231_RECORD_IRQ |
- CS4231_TIMER_IRQ);
- snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
- cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
- cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
- chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
- snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
- snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
- CS4231_RECORD_IRQ |
- CS4231_TIMER_IRQ);
- snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- chip->mode = mode;
- mutex_unlock(&chip->open_mutex);
- return 0;
-}
-
-static void snd_cs4231_close(struct snd_cs4231 *chip, unsigned int mode)
-{
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
- chip->mode &= ~mode;
- if (chip->mode & CS4231_MODE_OPEN) {
- mutex_unlock(&chip->open_mutex);
- return;
- }
- snd_cs4231_calibrate_mute(chip, 1);
-
- /* disable IRQ */
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
- cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
- cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
- chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
- snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
-
- /* now disable record & playback */
-
- if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
- CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
- CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
- snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- }
-
- /* clear IRQ again */
- snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
- cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
- cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- snd_cs4231_calibrate_mute(chip, 0);
-
- chip->mode = 0;
- mutex_unlock(&chip->open_mutex);
-}
-
-/*
- * timer open/close
- */
-
-static int snd_cs4231_timer_open(struct snd_timer * timer)
-{
- struct snd_cs4231 *chip = snd_timer_chip(timer);
- snd_cs4231_open(chip, CS4231_MODE_TIMER);
- return 0;
-}
-
-static int snd_cs4231_timer_close(struct snd_timer * timer)
-{
- struct snd_cs4231 *chip = snd_timer_chip(timer);
- snd_cs4231_close(chip, CS4231_MODE_TIMER);
- return 0;
-}
-
-static struct snd_timer_hardware snd_cs4231_timer_table =
-{
- .flags = SNDRV_TIMER_HW_AUTO,
- .resolution = 9945,
- .ticks = 65535,
- .open = snd_cs4231_timer_open,
- .close = snd_cs4231_timer_close,
- .c_resolution = snd_cs4231_timer_resolution,
- .start = snd_cs4231_timer_start,
- .stop = snd_cs4231_timer_stop,
-};
-
-/*
- * ok.. exported functions..
- */
-
-static int snd_cs4231_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- unsigned char new_pdfr;
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
- snd_cs4231_get_rate(params_rate(hw_params));
- chip->set_playback_format(chip, hw_params, new_pdfr);
- return 0;
-}
-
-static int snd_cs4231_playback_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
- unsigned int count = snd_pcm_lib_period_bytes(substream);
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->p_dma_size = size;
- chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
- snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
- count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
- snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
- snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-#if 0
- snd_cs4231_debug(chip);
-#endif
- return 0;
-}
-
-static int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- unsigned char new_cdfr;
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
- snd_cs4231_get_rate(params_rate(hw_params));
- chip->set_capture_format(chip, hw_params, new_cdfr);
- return 0;
-}
-
-static int snd_cs4231_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
- unsigned int count = snd_pcm_lib_period_bytes(substream);
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->c_dma_size = size;
- chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
- snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
- count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1;
- if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
- snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
- snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
- } else {
- snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
- snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8));
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static void snd_cs4231_overrange(struct snd_cs4231 *chip)
-{
- unsigned long flags;
- unsigned char res;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- res = snd_cs4231_in(chip, CS4231_TEST_INIT);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */
- chip->capture_substream->runtime->overrange++;
-}
-
-irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id)
-{
- struct snd_cs4231 *chip = dev_id;
- unsigned char status;
-
- status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
- if (status & CS4231_TIMER_IRQ) {
- if (chip->timer)
- snd_timer_interrupt(chip->timer, chip->timer->sticks);
- }
- if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
- if (status & CS4231_PLAYBACK_IRQ) {
- if (chip->mode & CS4231_MODE_PLAY) {
- if (chip->playback_substream)
- snd_pcm_period_elapsed(chip->playback_substream);
- }
- if (chip->mode & CS4231_MODE_RECORD) {
- if (chip->capture_substream) {
- snd_cs4231_overrange(chip);
- snd_pcm_period_elapsed(chip->capture_substream);
- }
- }
- }
- } else {
- if (status & CS4231_PLAYBACK_IRQ) {
- if (chip->playback_substream)
- snd_pcm_period_elapsed(chip->playback_substream);
- }
- if (status & CS4231_RECORD_IRQ) {
- if (chip->capture_substream) {
- snd_cs4231_overrange(chip);
- snd_pcm_period_elapsed(chip->capture_substream);
- }
- }
- }
-
- spin_lock(&chip->reg_lock);
- snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
- spin_unlock(&chip->reg_lock);
- return IRQ_HANDLED;
-}
-
-static snd_pcm_uframes_t snd_cs4231_playback_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- size_t ptr;
-
- if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
- return 0;
- ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
- return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_cs4231_capture_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- size_t ptr;
-
- if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
- return 0;
- ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
- return bytes_to_frames(substream->runtime, ptr);
-}
-
-/*
-
- */
-
-static int snd_cs4231_probe(struct snd_cs4231 *chip)
-{
- unsigned long flags;
- int i, id, rev;
- unsigned char *ptr;
- unsigned int hw;
-
-#if 0
- snd_cs4231_debug(chip);
-#endif
- id = 0;
- for (i = 0; i < 50; i++) {
- mb();
- if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- udelay(2000);
- else {
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
- id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (id == 0x0a)
- break; /* this is valid value */
- }
- }
- snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id);
- if (id != 0x0a)
- return -ENODEV; /* no valid device found */
-
- if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
- rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7;
- snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
- if (rev == 0x80) {
- unsigned char tmp = snd_cs4231_in(chip, 23);
- snd_cs4231_out(chip, 23, ~tmp);
- if (snd_cs4231_in(chip, 23) != tmp)
- chip->hardware = CS4231_HW_AD1845;
- else
- chip->hardware = CS4231_HW_CS4231;
- } else if (rev == 0xa0) {
- chip->hardware = CS4231_HW_CS4231A;
- } else if (rev == 0xa2) {
- chip->hardware = CS4231_HW_CS4232;
- } else if (rev == 0xb2) {
- chip->hardware = CS4231_HW_CS4232A;
- } else if (rev == 0x83) {
- chip->hardware = CS4231_HW_CS4236;
- } else if (rev == 0x03) {
- chip->hardware = CS4231_HW_CS4236B;
- } else {
- snd_printk("unknown CS chip with version 0x%x\n", rev);
- return -ENODEV; /* unknown CS4231 chip? */
- }
- }
- spin_lock_irqsave(&chip->reg_lock, flags);
- cs4231_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
- cs4231_outb(chip, CS4231P(STATUS), 0);
- mb();
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
- switch (chip->hardware) {
- case CS4231_HW_INTERWAVE:
- chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
- break;
- case CS4231_HW_CS4235:
- case CS4231_HW_CS4236B:
- case CS4231_HW_CS4237B:
- case CS4231_HW_CS4238B:
- case CS4231_HW_CS4239:
- if (hw == CS4231_HW_DETECT3)
- chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
- else
- chip->hardware = CS4231_HW_CS4236;
- break;
- }
-
- chip->image[CS4231_IFACE_CTRL] =
- (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
- (chip->single_dma ? CS4231_SINGLE_DMA : 0);
- chip->image[CS4231_ALT_FEATURE_1] = 0x80;
- chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
- ptr = (unsigned char *) &chip->image;
- snd_cs4231_mce_down(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */
- snd_cs4231_out(chip, i, *ptr++);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_up(chip);
- snd_cs4231_mce_down(chip);
-
- mdelay(2);
-
- /* ok.. try check hardware version for CS4236+ chips */
- if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
- if (chip->hardware == CS4231_HW_CS4236B) {
- rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
- snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
- id = snd_cs4236_ext_in(chip, CS4236_VERSION);
- snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
- snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
- if ((id & 0x1f) == 0x1d) { /* CS4235 */
- chip->hardware = CS4231_HW_CS4235;
- switch (id >> 5) {
- case 4:
- case 5:
- case 6:
- break;
- default:
- snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
- }
- } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */
- switch (id >> 5) {
- case 4:
- case 5:
- case 6:
- case 7:
- chip->hardware = CS4231_HW_CS4236B;
- break;
- default:
- snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
- }
- } else if ((id & 0x1f) == 0x08) { /* CS4237B */
- chip->hardware = CS4231_HW_CS4237B;
- switch (id >> 5) {
- case 4:
- case 5:
- case 6:
- case 7:
- break;
- default:
- snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
- }
- } else if ((id & 0x1f) == 0x09) { /* CS4238B */
- chip->hardware = CS4231_HW_CS4238B;
- switch (id >> 5) {
- case 5:
- case 6:
- case 7:
- break;
- default:
- snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
- }
- } else if ((id & 0x1f) == 0x1e) { /* CS4239 */
- chip->hardware = CS4231_HW_CS4239;
- switch (id >> 5) {
- case 4:
- case 5:
- case 6:
- break;
- default:
- snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
- }
- } else {
- snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
- }
- }
- }
- return 0; /* all things are ok.. */
-}
-
-/*
-
- */
-
-static struct snd_pcm_hardware snd_cs4231_playback =
-{
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_SYNC_START),
- .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
- .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5510,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_cs4231_capture =
-{
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_SYNC_START),
- .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
- .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5510,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
-/*
-
- */
-
-static int snd_cs4231_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- runtime->hw = snd_cs4231_playback;
-
- /* hardware bug in InterWave chipset */
- if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3)
- runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
-
- /* hardware limitation of cheap chips */
- if (chip->hardware == CS4231_HW_CS4235 ||
- chip->hardware == CS4231_HW_CS4239)
- runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
-
- snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
- snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
-
- if (chip->claim_dma) {
- if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
- return err;
- }
-
- if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) {
- if (chip->release_dma)
- chip->release_dma(chip, chip->dma_private_data, chip->dma1);
- snd_free_pages(runtime->dma_area, runtime->dma_bytes);
- return err;
- }
- chip->playback_substream = substream;
- snd_pcm_set_sync(substream);
- chip->rate_constraint(runtime);
- return 0;
-}
-
-static int snd_cs4231_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- runtime->hw = snd_cs4231_capture;
-
- /* hardware limitation of cheap chips */
- if (chip->hardware == CS4231_HW_CS4235 ||
- chip->hardware == CS4231_HW_CS4239)
- runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
-
- snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
- snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
-
- if (chip->claim_dma) {
- if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
- return err;
- }
-
- if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) {
- if (chip->release_dma)
- chip->release_dma(chip, chip->dma_private_data, chip->dma2);
- snd_free_pages(runtime->dma_area, runtime->dma_bytes);
- return err;
- }
- chip->capture_substream = substream;
- snd_pcm_set_sync(substream);
- chip->rate_constraint(runtime);
- return 0;
-}
-
-static int snd_cs4231_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-
- chip->playback_substream = NULL;
- snd_cs4231_close(chip, CS4231_MODE_PLAY);
- return 0;
-}
-
-static int snd_cs4231_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-
- chip->capture_substream = NULL;
- snd_cs4231_close(chip, CS4231_MODE_RECORD);
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-/* lowlevel suspend callback for CS4231 */
-static void snd_cs4231_suspend(struct snd_cs4231 *chip)
-{
- int reg;
- unsigned long flags;
-
- snd_pcm_suspend_all(chip->pcm);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++)
- chip->image[reg] = snd_cs4231_in(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-/* lowlevel resume callback for CS4231 */
-static void snd_cs4231_resume(struct snd_cs4231 *chip)
-{
- int reg;
- unsigned long flags;
- /* int timeout; */
-
- snd_cs4231_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++) {
- switch (reg) {
- case CS4231_VERSION:
- break;
- default:
- snd_cs4231_out(chip, reg, chip->image[reg]);
- break;
- }
- }
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-#if 1
- snd_cs4231_mce_down(chip);
-#else
- /* The following is a workaround to avoid freeze after resume on TP600E.
- This is the first half of copy of snd_cs4231_mce_down(), but doesn't
- include rescheduling. -- iwai
- */
- snd_cs4231_busy_wait(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit &= ~CS4231_MCE;
- timeout = cs4231_inb(chip, CS4231P(REGSEL));
- cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (timeout == 0x80)
- snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
- if ((timeout & CS4231_MCE) == 0 ||
- !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
- return;
- }
- snd_cs4231_busy_wait(chip);
-#endif
-}
-#endif /* CONFIG_PM */
-
-static int snd_cs4231_free(struct snd_cs4231 *chip)
-{
- release_and_free_resource(chip->res_port);
- release_and_free_resource(chip->res_cport);
- if (chip->irq >= 0) {
- disable_irq(chip->irq);
- if (!(chip->hwshare & CS4231_HWSHARE_IRQ))
- free_irq(chip->irq, (void *) chip);
- }
- if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) {
- snd_dma_disable(chip->dma1);
- free_dma(chip->dma1);
- }
- if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
- snd_dma_disable(chip->dma2);
- free_dma(chip->dma2);
- }
- if (chip->timer)
- snd_device_free(chip->card, chip->timer);
- kfree(chip);
- return 0;
-}
-
-static int snd_cs4231_dev_free(struct snd_device *device)
-{
- struct snd_cs4231 *chip = device->device_data;
- return snd_cs4231_free(chip);
-}
-
-const char *snd_cs4231_chip_id(struct snd_cs4231 *chip)
-{
- switch (chip->hardware) {
- case CS4231_HW_CS4231: return "CS4231";
- case CS4231_HW_CS4231A: return "CS4231A";
- case CS4231_HW_CS4232: return "CS4232";
- case CS4231_HW_CS4232A: return "CS4232A";
- case CS4231_HW_CS4235: return "CS4235";
- case CS4231_HW_CS4236: return "CS4236";
- case CS4231_HW_CS4236B: return "CS4236B";
- case CS4231_HW_CS4237B: return "CS4237B";
- case CS4231_HW_CS4238B: return "CS4238B";
- case CS4231_HW_CS4239: return "CS4239";
- case CS4231_HW_INTERWAVE: return "AMD InterWave";
- case CS4231_HW_OPL3SA2: return chip->card->shortname;
- case CS4231_HW_AD1845: return "AD1845";
- default: return "???";
- }
-}
-
-static int snd_cs4231_new(struct snd_card *card,
- unsigned short hardware,
- unsigned short hwshare,
- struct snd_cs4231 ** rchip)
-{
- struct snd_cs4231 *chip;
-
- *rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
- chip->hardware = hardware;
- chip->hwshare = hwshare;
-
- spin_lock_init(&chip->reg_lock);
- mutex_init(&chip->mce_mutex);
- mutex_init(&chip->open_mutex);
- chip->card = card;
- chip->rate_constraint = snd_cs4231_xrate;
- chip->set_playback_format = snd_cs4231_playback_format;
- chip->set_capture_format = snd_cs4231_capture_format;
- memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image));
-
- *rchip = chip;
- return 0;
-}
-
-int snd_cs4231_create(struct snd_card *card,
- unsigned long port,
- unsigned long cport,
- int irq, int dma1, int dma2,
- unsigned short hardware,
- unsigned short hwshare,
- struct snd_cs4231 ** rchip)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_cs4231_dev_free,
- };
- struct snd_cs4231 *chip;
- int err;
-
- err = snd_cs4231_new(card, hardware, hwshare, &chip);
- if (err < 0)
- return err;
-
- chip->irq = -1;
- chip->dma1 = -1;
- chip->dma2 = -1;
-
- if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) {
- snd_printk(KERN_ERR "cs4231: can't grab port 0x%lx\n", port);
- snd_cs4231_free(chip);
- return -EBUSY;
- }
- chip->port = port;
- if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) {
- snd_printk(KERN_ERR "cs4231: can't grab control port 0x%lx\n", cport);
- snd_cs4231_free(chip);
- return -ENODEV;
- }
- chip->cport = cport;
- if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, IRQF_DISABLED, "CS4231", (void *) chip)) {
- snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq);
- snd_cs4231_free(chip);
- return -EBUSY;
- }
- chip->irq = irq;
- if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) {
- snd_printk(KERN_ERR "cs4231: can't grab DMA1 %d\n", dma1);
- snd_cs4231_free(chip);
- return -EBUSY;
- }
- chip->dma1 = dma1;
- if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) {
- snd_printk(KERN_ERR "cs4231: can't grab DMA2 %d\n", dma2);
- snd_cs4231_free(chip);
- return -EBUSY;
- }
- if (dma1 == dma2 || dma2 < 0) {
- chip->single_dma = 1;
- chip->dma2 = chip->dma1;
- } else
- chip->dma2 = dma2;
-
- /* global setup */
- if (snd_cs4231_probe(chip) < 0) {
- snd_cs4231_free(chip);
- return -ENODEV;
- }
- snd_cs4231_init(chip);
-
-#if 0
- if (chip->hardware & CS4231_HW_CS4232_MASK) {
- if (chip->res_cport == NULL)
- snd_printk("CS4232 control port features are not accessible\n");
- }
-#endif
-
- /* Register device */
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_cs4231_free(chip);
- return err;
- }
-
-#ifdef CONFIG_PM
- /* Power Management */
- chip->suspend = snd_cs4231_suspend;
- chip->resume = snd_cs4231_resume;
-#endif
-
- *rchip = chip;
- return 0;
-}
-
-static struct snd_pcm_ops snd_cs4231_playback_ops = {
- .open = snd_cs4231_playback_open,
- .close = snd_cs4231_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cs4231_playback_hw_params,
- .hw_free = snd_cs4231_playback_hw_free,
- .prepare = snd_cs4231_playback_prepare,
- .trigger = snd_cs4231_trigger,
- .pointer = snd_cs4231_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_cs4231_capture_ops = {
- .open = snd_cs4231_capture_open,
- .close = snd_cs4231_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cs4231_capture_hw_params,
- .hw_free = snd_cs4231_capture_hw_free,
- .prepare = snd_cs4231_capture_prepare,
- .trigger = snd_cs4231_trigger,
- .pointer = snd_cs4231_capture_pointer,
-};
-
-int snd_cs4231_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm)
-{
- struct snd_pcm *pcm;
- int err;
-
- if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0)
- return err;
-
- spin_lock_init(&chip->reg_lock);
- mutex_init(&chip->mce_mutex);
- mutex_init(&chip->open_mutex);
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops);
-
- /* global setup */
- pcm->private_data = chip;
- pcm->info_flags = 0;
- if (chip->single_dma)
- pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
- if (chip->hardware != CS4231_HW_INTERWAVE)
- pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
- strcpy(pcm->name, snd_cs4231_chip_id(chip));
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
-
- chip->pcm = pcm;
- if (rpcm)
- *rpcm = pcm;
- return 0;
-}
-
-static void snd_cs4231_timer_free(struct snd_timer *timer)
-{
- struct snd_cs4231 *chip = timer->private_data;
- chip->timer = NULL;
-}
-
-int snd_cs4231_timer(struct snd_cs4231 *chip, int device, struct snd_timer **rtimer)
-{
- struct snd_timer *timer;
- struct snd_timer_id tid;
- int err;
-
- /* Timer initialization */
- tid.dev_class = SNDRV_TIMER_CLASS_CARD;
- tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
- tid.card = chip->card->number;
- tid.device = device;
- tid.subdevice = 0;
- if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
- return err;
- strcpy(timer->name, snd_cs4231_chip_id(chip));
- timer->private_data = chip;
- timer->private_free = snd_cs4231_timer_free;
- timer->hw = snd_cs4231_timer_table;
- chip->timer = timer;
- if (rtimer)
- *rtimer = timer;
- return 0;
-}
-
-/*
- * MIXER part
- */
-
-static int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[4] = {
- "Line", "Aux", "Mic", "Mix"
- };
- static char *opl3sa_texts[4] = {
- "Line", "CD", "Mic", "Mix"
- };
- static char *gusmax_texts[4] = {
- "Line", "Synth", "Mic", "Mix"
- };
- char **ptexts = texts;
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-
- snd_assert(chip->card != NULL, return -EINVAL);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- if (!strcmp(chip->card->driver, "GUS MAX"))
- ptexts = gusmax_texts;
- switch (chip->hardware) {
- case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break;
- case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break;
- }
- strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
- ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static int snd_cs4231_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- unsigned short left, right;
- int change;
-
- if (ucontrol->value.enumerated.item[0] > 3 ||
- ucontrol->value.enumerated.item[1] > 3)
- return -EINVAL;
- left = ucontrol->value.enumerated.item[0] << 6;
- right = ucontrol->value.enumerated.item[1] << 6;
- spin_lock_irqsave(&chip->reg_lock, flags);
- left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
- right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
- change = left != chip->image[CS4231_LEFT_INPUT] ||
- right != chip->image[CS4231_RIGHT_INPUT];
- snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
- snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return change;
-}
-
-int snd_cs4231_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-int snd_cs4231_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- return 0;
-}
-
-int snd_cs4231_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
- int change;
- unsigned short val;
-
- val = (ucontrol->value.integer.value[0] & mask);
- if (invert)
- val = mask - val;
- val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
- val = (chip->image[reg] & ~(mask << shift)) | val;
- change = val != chip->image[reg];
- snd_cs4231_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return change;
-}
-
-int snd_cs4231_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 24) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-int snd_cs4231_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int left_reg = kcontrol->private_value & 0xff;
- int right_reg = (kcontrol->private_value >> 8) & 0xff;
- int shift_left = (kcontrol->private_value >> 16) & 0x07;
- int shift_right = (kcontrol->private_value >> 19) & 0x07;
- int mask = (kcontrol->private_value >> 24) & 0xff;
- int invert = (kcontrol->private_value >> 22) & 1;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
- ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (invert) {
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
- }
- return 0;
-}
-
-int snd_cs4231_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int left_reg = kcontrol->private_value & 0xff;
- int right_reg = (kcontrol->private_value >> 8) & 0xff;
- int shift_left = (kcontrol->private_value >> 16) & 0x07;
- int shift_right = (kcontrol->private_value >> 19) & 0x07;
- int mask = (kcontrol->private_value >> 24) & 0xff;
- int invert = (kcontrol->private_value >> 22) & 1;
- int change;
- unsigned short val1, val2;
-
- val1 = ucontrol->value.integer.value[0] & mask;
- val2 = ucontrol->value.integer.value[1] & mask;
- if (invert) {
- val1 = mask - val1;
- val2 = mask - val2;
- }
- val1 <<= shift_left;
- val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
- val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
- val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
- change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
- snd_cs4231_out(chip, left_reg, val1);
- snd_cs4231_out(chip, right_reg, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return change;
-}
-
-static struct snd_kcontrol_new snd_cs4231_controls[] = {
-CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
-CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
-CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
-CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
-CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = snd_cs4231_info_mux,
- .get = snd_cs4231_get_mux,
- .put = snd_cs4231_put_mux,
-},
-CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1)
-};
-
-int snd_cs4231_mixer(struct snd_cs4231 *chip)
-{
- struct snd_card *card;
- unsigned int idx;
- int err;
-
- snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
-
- card = chip->card;
-
- strcpy(card->mixername, chip->pcm->name);
-
- for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0)
- return err;
- }
- return 0;
-}
-
-EXPORT_SYMBOL(snd_cs4231_out);
-EXPORT_SYMBOL(snd_cs4231_in);
-EXPORT_SYMBOL(snd_cs4236_ext_out);
-EXPORT_SYMBOL(snd_cs4236_ext_in);
-EXPORT_SYMBOL(snd_cs4231_mce_up);
-EXPORT_SYMBOL(snd_cs4231_mce_down);
-EXPORT_SYMBOL(snd_cs4231_interrupt);
-EXPORT_SYMBOL(snd_cs4231_chip_id);
-EXPORT_SYMBOL(snd_cs4231_create);
-EXPORT_SYMBOL(snd_cs4231_pcm);
-EXPORT_SYMBOL(snd_cs4231_mixer);
-EXPORT_SYMBOL(snd_cs4231_timer);
-EXPORT_SYMBOL(snd_cs4231_info_single);
-EXPORT_SYMBOL(snd_cs4231_get_single);
-EXPORT_SYMBOL(snd_cs4231_put_single);
-EXPORT_SYMBOL(snd_cs4231_info_double);
-EXPORT_SYMBOL(snd_cs4231_get_double);
-EXPORT_SYMBOL(snd_cs4231_put_double);
-
-/*
- * INIT part
- */
-
-static int __init alsa_cs4231_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_cs4231_exit(void)
-{
-}
-
-module_init(alsa_cs4231_init)
-module_exit(alsa_cs4231_exit)
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index dbe63db4bfd..91f9c15d3e3 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -26,7 +26,7 @@
#include <linux/pnp.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
@@ -134,7 +134,7 @@ static int pnp_registered;
#endif /* CONFIG_PNP */
struct snd_card_cs4236 {
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
struct resource *res_sb_port;
#ifdef CONFIG_PNP
struct pnp_dev *wss;
@@ -239,6 +239,8 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
{ .id = "CSC9836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
/* Gallant SC-70P */
{ .id = "CSC9837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
+ /* Techmakers MF-4236PW */
+ { .id = "CSCa736", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
/* TerraTec AudioSystem EWS64XL - CS4236B */
{ .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" }, { "CSCa803" } } },
/* TerraTec AudioSystem EWS64XL - CS4236B */
@@ -325,6 +327,7 @@ 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)
{
+ acard->wss = pdev;
if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
return -EBUSY;
cport[dev] = -1;
@@ -395,7 +398,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
{
struct snd_card_cs4236 *acard;
struct snd_pcm *pcm;
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
struct snd_opl3 *opl3;
int err;
@@ -407,41 +410,37 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
}
#ifdef CS4232
- if ((err = snd_cs4231_create(card,
- port[dev],
- cport[dev],
- irq[dev],
- dma1[dev],
- dma2[dev],
- CS4231_HW_DETECT,
- 0,
- &chip)) < 0)
+ err = snd_wss_create(card, port[dev], cport[dev],
+ irq[dev],
+ dma1[dev], dma2[dev],
+ WSS_HW_DETECT, 0, &chip);
+ if (err < 0)
return err;
acard->chip = chip;
- if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0)
+ err = snd_wss_pcm(chip, 0, &pcm);
+ if (err < 0)
return err;
- if ((err = snd_cs4231_mixer(chip)) < 0)
+ err = snd_wss_mixer(chip);
+ if (err < 0)
return err;
#else /* CS4236 */
- if ((err = snd_cs4236_create(card,
- port[dev],
- cport[dev],
- irq[dev],
- dma1[dev],
- dma2[dev],
- CS4231_HW_DETECT,
- 0,
- &chip)) < 0)
+ err = snd_cs4236_create(card,
+ port[dev], cport[dev],
+ irq[dev], dma1[dev], dma2[dev],
+ WSS_HW_DETECT, 0, &chip);
+ if (err < 0)
return err;
acard->chip = chip;
- if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0)
+ err = snd_cs4236_pcm(chip, 0, &pcm);
+ if (err < 0)
return err;
- if ((err = snd_cs4236_mixer(chip)) < 0)
+ err = snd_cs4236_mixer(chip);
+ if (err < 0)
return err;
#endif
strcpy(card->driver, pcm->name);
@@ -454,7 +453,8 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (dma2[dev] >= 0)
sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]);
- if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0)
+ err = snd_wss_timer(chip, 0, NULL);
+ if (err < 0)
return err;
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index de71910401e..6a85fdc53b6 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -85,7 +85,7 @@
#include <linux/time.h>
#include <linux/wait.h>
#include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/asoundef.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -121,13 +121,14 @@ static unsigned char snd_cs4236_ext_map[18] = {
*
*/
-static void snd_cs4236_ctrl_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val)
+static void snd_cs4236_ctrl_out(struct snd_wss *chip,
+ unsigned char reg, unsigned char val)
{
outb(reg, chip->cport + 3);
outb(chip->cimage[reg] = val, chip->cport + 4);
}
-static unsigned char snd_cs4236_ctrl_in(struct snd_cs4231 *chip, unsigned char reg)
+static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg)
{
outb(reg, chip->cport + 3);
return inb(chip->cport + 4);
@@ -180,44 +181,52 @@ static unsigned char divisor_to_rate_register(unsigned int divisor)
}
}
-static void snd_cs4236_playback_format(struct snd_cs4231 *chip, struct snd_pcm_hw_params *params, unsigned char pdfr)
+static void snd_cs4236_playback_format(struct snd_wss *chip,
+ struct snd_pcm_hw_params *params,
+ unsigned char pdfr)
{
unsigned long flags;
unsigned char rate = divisor_to_rate_register(params->rate_den);
spin_lock_irqsave(&chip->reg_lock, flags);
/* set fast playback format change and clean playback FIFO */
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static void snd_cs4236_capture_format(struct snd_cs4231 *chip, struct snd_pcm_hw_params *params, unsigned char cdfr)
+static void snd_cs4236_capture_format(struct snd_wss *chip,
+ struct snd_pcm_hw_params *params,
+ unsigned char cdfr)
{
unsigned long flags;
unsigned char rate = divisor_to_rate_register(params->rate_den);
spin_lock_irqsave(&chip->reg_lock, flags);
/* set fast capture format change and clean capture FIFO */
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
- snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+ snd_wss_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
#ifdef CONFIG_PM
-static void snd_cs4236_suspend(struct snd_cs4231 *chip)
+static void snd_cs4236_suspend(struct snd_wss *chip)
{
int reg;
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
for (reg = 0; reg < 32; reg++)
- chip->image[reg] = snd_cs4231_in(chip, reg);
+ chip->image[reg] = snd_wss_in(chip, reg);
for (reg = 0; reg < 18; reg++)
chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
for (reg = 2; reg < 9; reg++)
@@ -225,12 +234,12 @@ static void snd_cs4236_suspend(struct snd_cs4231 *chip)
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static void snd_cs4236_resume(struct snd_cs4231 *chip)
+static void snd_cs4236_resume(struct snd_wss *chip)
{
int reg;
unsigned long flags;
- snd_cs4231_mce_up(chip);
+ snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
for (reg = 0; reg < 32; reg++) {
switch (reg) {
@@ -240,7 +249,7 @@ static void snd_cs4236_resume(struct snd_cs4231 *chip)
case 29: /* why? CS4235 - master right */
break;
default:
- snd_cs4231_out(chip, reg, chip->image[reg]);
+ snd_wss_out(chip, reg, chip->image[reg]);
break;
}
}
@@ -255,7 +264,7 @@ static void snd_cs4236_resume(struct snd_cs4231 *chip)
}
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
+ snd_wss_mce_down(chip);
}
#endif /* CONFIG_PM */
@@ -266,24 +275,26 @@ int snd_cs4236_create(struct snd_card *card,
int irq, int dma1, int dma2,
unsigned short hardware,
unsigned short hwshare,
- struct snd_cs4231 ** rchip)
+ struct snd_wss **rchip)
{
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
unsigned char ver1, ver2;
unsigned int reg;
int err;
*rchip = NULL;
- if (hardware == CS4231_HW_DETECT)
- hardware = CS4231_HW_DETECT3;
+ if (hardware == WSS_HW_DETECT)
+ hardware = WSS_HW_DETECT3;
if (cport < 0x100) {
snd_printk("please, specify control port for CS4236+ chips\n");
return -ENODEV;
}
- if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0)
+ err = snd_wss_create(card, port, cport,
+ irq, dma1, dma2, hardware, hwshare, &chip);
+ if (err < 0)
return err;
- if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) {
+ if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%x\n",chip->hardware);
snd_device_free(card, chip);
return -ENODEV;
@@ -330,20 +341,20 @@ int snd_cs4236_create(struct snd_card *card,
snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
/* initialize compatible but more featured registers */
- snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40);
- snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40);
- snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
- snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
- snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
- snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
- snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
- snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff);
- snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+ snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
+ snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
+ snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
+ snd_wss_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
+ snd_wss_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
+ snd_wss_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
+ snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+ snd_wss_out(chip, CS4231_LEFT_LINE_IN, 0xff);
+ snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
switch (chip->hardware) {
- case CS4231_HW_CS4235:
- case CS4231_HW_CS4239:
- snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff);
- snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff);
+ case WSS_HW_CS4235:
+ case WSS_HW_CS4239:
+ snd_wss_out(chip, CS4235_LEFT_MASTER, 0xff);
+ snd_wss_out(chip, CS4235_RIGHT_MASTER, 0xff);
break;
}
@@ -351,12 +362,13 @@ int snd_cs4236_create(struct snd_card *card,
return 0;
}
-int snd_cs4236_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm)
+int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
- if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0)
+ err = snd_wss_pcm(chip, device, &pcm);
+ if (err < 0)
return err;
pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
if (rpcm)
@@ -387,7 +399,7 @@ static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -404,7 +416,7 @@ static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -433,7 +445,7 @@ static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -450,7 +462,7 @@ static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -490,7 +502,7 @@ static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -512,7 +524,7 @@ static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -555,7 +567,7 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -577,7 +589,7 @@ static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -600,7 +612,7 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
- snd_cs4231_out(chip, left_reg, val1);
+ snd_wss_out(chip, left_reg, val1);
snd_cs4236_ext_out(chip, right_reg, val2);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
@@ -619,7 +631,7 @@ static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -631,7 +643,7 @@ static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct s
static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int change;
unsigned short val1, val2;
@@ -678,7 +690,7 @@ static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)
static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -690,7 +702,7 @@ static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_
static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int change;
unsigned short val1, val2;
@@ -701,108 +713,160 @@ static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_
val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
- snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1);
- snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2);
+ snd_wss_out(chip, CS4235_LEFT_MASTER, val1);
+ snd_wss_out(chip, CS4235_RIGHT_MASTER, val2);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
static struct snd_kcontrol_new snd_cs4236_controls[] = {
-CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_DOUBLE("Master Digital Playback Switch", 0,
+ CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0,
+ CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
-CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
-
-CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
-
-CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
-CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
-
-CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
-CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
-
-CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
-CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
-
-CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
-
-CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
-CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Capture Boost Volume", 0,
+ CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+
+WSS_DOUBLE("PCM Playback Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+
+CS4236_DOUBLE("DSP Playback Switch", 0,
+ CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
+CS4236_DOUBLE("DSP Playback Volume", 0,
+ CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+
+CS4236_DOUBLE("FM Playback Switch", 0,
+ CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
+CS4236_DOUBLE("FM Playback Volume", 0,
+ CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+
+CS4236_DOUBLE("Wavetable Playback Switch", 0,
+ CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+CS4236_DOUBLE("Wavetable Playback Volume", 0,
+ CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+
+WSS_DOUBLE("Synth Playback Switch", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Synth Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE("Synth Capture Switch", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+WSS_DOUBLE("Synth Capture Bypass", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
+
+CS4236_DOUBLE("Mic Playback Switch", 0,
+ CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_DOUBLE("Mic Capture Switch", 0,
+ CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
-CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
-
-CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
-
-CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
-
-CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
-CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
-
-CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
-
-CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+CS4236_DOUBLE("Mic Playback Boost", 0,
+ CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
+
+WSS_DOUBLE("Line Playback Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Line Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Line Capture Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("Line Capture Bypass", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
+
+WSS_DOUBLE("CD Playback Switch", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("CD Volume", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("CD Capture Switch", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+
+CS4236_DOUBLE1("Mono Output Playback Switch", 0,
+ CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+CS4236_DOUBLE1("Mono Playback Switch", 0,
+ CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+
+WSS_DOUBLE("Capture Volume", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+WSS_DOUBLE("Analog Loopback Capture Switch", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+
+WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1("Digital Loopback Playback Volume", 0,
+ CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
};
static struct snd_kcontrol_new snd_cs4235_controls[] = {
-CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
+WSS_DOUBLE("Master Switch", 0,
+ CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
+WSS_DOUBLE("Master Volume", 0,
+ CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
CS4235_OUTPUT_ACCU("Playback Volume", 0),
-CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_DOUBLE("Master Digital Playback Switch", 0,
+ CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0,
+ CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
-CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE("Master Digital Playback Switch", 1,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Master Digital Capture Switch", 1,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+WSS_DOUBLE("Master Digital Volume", 1,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE("Capture Volume", 0,
+ CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
-CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("PCM Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
-CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+CS4236_DOUBLE("Wavetable Switch", 0,
+ CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
-CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
-CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_DOUBLE("Mic Capture Switch", 0,
+ CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Playback Switch", 0,
+ CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
-CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-
-CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
-CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-
-CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
-
-CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-
-CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+WSS_DOUBLE("Aux Playback Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Capture Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("Aux Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+
+WSS_DOUBLE("Aux Playback Switch", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Capture Switch", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("Aux Volume", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+
+CS4236_DOUBLE1("Master Mono Switch", 0,
+ CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+
+CS4236_DOUBLE1("Mono Switch", 0,
+ CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+
+WSS_DOUBLE("Analog Loopback Switch", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
};
#define CS4236_IEC958_ENABLE(xname, xindex) \
@@ -813,14 +877,14 @@ CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT
static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
#if 0
printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
- snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+ snd_wss_in(chip, CS4231_ALT_FEATURE_1),
snd_cs4236_ctrl_in(chip, 3),
snd_cs4236_ctrl_in(chip, 4),
snd_cs4236_ctrl_in(chip, 5),
@@ -833,7 +897,7 @@ static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct sn
static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int change;
unsigned short enable, val;
@@ -841,23 +905,23 @@ static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct sn
enable = ucontrol->value.integer.value[0] & 1;
mutex_lock(&chip->mce_mutex);
- snd_cs4231_mce_up(chip);
+ snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
change = val != chip->image[CS4231_ALT_FEATURE_1];
- snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
snd_cs4236_ctrl_out(chip, 4, val);
udelay(100);
val &= ~0x40;
snd_cs4236_ctrl_out(chip, 4, val);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
+ snd_wss_mce_down(chip);
mutex_unlock(&chip->mce_mutex);
#if 0
printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
- snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+ snd_wss_in(chip, CS4231_ALT_FEATURE_1),
snd_cs4236_ctrl_in(chip, 3),
snd_cs4236_ctrl_in(chip, 4),
snd_cs4236_ctrl_in(chip, 5),
@@ -896,19 +960,20 @@ CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),
CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
};
-int snd_cs4236_mixer(struct snd_cs4231 *chip)
+int snd_cs4236_mixer(struct snd_wss *chip)
{
struct snd_card *card;
unsigned int idx, count;
int err;
struct snd_kcontrol_new *kcontrol;
- snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip || !chip->card))
+ return -EINVAL;
card = chip->card;
- strcpy(card->mixername, snd_cs4231_chip_id(chip));
+ strcpy(card->mixername, snd_wss_chip_id(chip));
- if (chip->hardware == CS4231_HW_CS4235 ||
- chip->hardware == CS4231_HW_CS4239) {
+ if (chip->hardware == WSS_HW_CS4235 ||
+ chip->hardware == WSS_HW_CS4239) {
for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) {
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)
return err;
@@ -920,16 +985,16 @@ int snd_cs4236_mixer(struct snd_cs4231 *chip)
}
}
switch (chip->hardware) {
- case CS4231_HW_CS4235:
- case CS4231_HW_CS4239:
+ case WSS_HW_CS4235:
+ case WSS_HW_CS4239:
count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235);
kcontrol = snd_cs4236_3d_controls_cs4235;
break;
- case CS4231_HW_CS4237B:
+ case WSS_HW_CS4237B:
count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237);
kcontrol = snd_cs4236_3d_controls_cs4237;
break;
- case CS4231_HW_CS4238B:
+ case WSS_HW_CS4238B:
count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238);
kcontrol = snd_cs4236_3d_controls_cs4238;
break;
@@ -941,8 +1006,8 @@ int snd_cs4236_mixer(struct snd_cs4231 *chip)
if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
return err;
}
- if (chip->hardware == CS4231_HW_CS4237B ||
- chip->hardware == CS4231_HW_CS4238B) {
+ if (chip->hardware == WSS_HW_CS4237B ||
+ chip->hardware == WSS_HW_CS4238B) {
for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) {
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)
return err;
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 1e1e575b1db..4fbb508a817 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -1009,7 +1009,8 @@ int snd_es1688_mixer(struct snd_es1688 *chip)
int err;
unsigned char reg, val;
- snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip || !chip->card))
+ return -EINVAL;
card = chip->card;
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index cccc16c8113..12eb98f2f93 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -276,9 +276,11 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
static unsigned char dmas[8] =
{6, 1, 0, 2, 0, 3, 4, 5};
- snd_assert(gus != NULL, return -EINVAL);
+ if (snd_BUG_ON(!gus))
+ return -EINVAL;
card = gus->card;
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
gus->mix_cntrl_reg &= 0xf8;
gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
index ebdb3346930..0dd43414016 100644
--- a/sound/isa/gus/gus_mixer.c
+++ b/sound/isa/gus/gus_mixer.c
@@ -161,9 +161,11 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus)
unsigned int idx, max;
int err;
- snd_assert(gus != NULL, return -EINVAL);
+ if (snd_BUG_ON(!gus))
+ return -EINVAL;
card = gus->card;
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
if (gus->ics_flag)
snd_component_add(card, "ICS2101");
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index 99731dc9732..38510aeb21c 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -352,8 +352,10 @@ static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
len = samples_to_bytes(runtime, count);
- snd_assert(bpos <= pcmp->dma_size, return -EIO);
- snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+ if (snd_BUG_ON(bpos > pcmp->dma_size))
+ return -EIO;
+ if (snd_BUG_ON(bpos + len > pcmp->dma_size))
+ return -EIO;
if (copy_from_user(runtime->dma_area + bpos, src, len))
return -EFAULT;
if (snd_gf1_pcm_use_dma && len > 32) {
@@ -381,8 +383,10 @@ static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
len = samples_to_bytes(runtime, count);
- snd_assert(bpos <= pcmp->dma_size, return -EIO);
- snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+ if (snd_BUG_ON(bpos > pcmp->dma_size))
+ return -EIO;
+ if (snd_BUG_ON(bpos + len > pcmp->dma_size))
+ return -EIO;
snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count);
if (snd_gf1_pcm_use_dma && len > 32) {
return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index f87c6236661..f94c1976e63 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -28,7 +28,7 @@
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
@@ -75,7 +75,7 @@ struct snd_gusmax {
int irq;
struct snd_card *card;
struct snd_gus_card *gus;
- struct snd_cs4231 *cs4231;
+ struct snd_wss *wss;
unsigned short gus_status_reg;
unsigned short pcm_status_reg;
};
@@ -117,7 +117,7 @@ static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id)
}
if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */
handled = 1;
- snd_cs4231_interrupt(irq, maxcard->cs4231);
+ snd_wss_interrupt(irq, maxcard->wss);
loop++;
}
} while (loop && --max > 0);
@@ -140,10 +140,7 @@ static void __devinit snd_gusmax_init(int dev, struct snd_card *card,
outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
}
-#define CS4231_PRIVATE( left, right, shift, mute ) \
- ((left << 24)|(right << 16)|(shift<<8)|mute)
-
-static int __devinit snd_gusmax_mixer(struct snd_cs4231 *chip)
+static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -214,7 +211,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
int xirq, xdma1, xdma2, err;
struct snd_card *card;
struct snd_gus_card *gus = NULL;
- struct snd_cs4231 *cs4231;
+ struct snd_wss *wss;
struct snd_gusmax *maxcard;
card = snd_card_new(index[dev], id[dev], THIS_MODULE,
@@ -301,33 +298,39 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
}
maxcard->irq = xirq;
- if ((err = snd_cs4231_create(card,
- gus->gf1.port + 0x10c, -1, xirq,
- xdma2 < 0 ? xdma1 : xdma2, xdma1,
- CS4231_HW_DETECT,
- CS4231_HWSHARE_IRQ |
- CS4231_HWSHARE_DMA1 |
- CS4231_HWSHARE_DMA2,
- &cs4231)) < 0)
+ err = snd_wss_create(card,
+ gus->gf1.port + 0x10c, -1, xirq,
+ xdma2 < 0 ? xdma1 : xdma2, xdma1,
+ WSS_HW_DETECT,
+ WSS_HWSHARE_IRQ |
+ WSS_HWSHARE_DMA1 |
+ WSS_HWSHARE_DMA2,
+ &wss);
+ if (err < 0)
goto _err;
- if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0)
+ err = snd_wss_pcm(wss, 0, NULL);
+ if (err < 0)
goto _err;
- if ((err = snd_cs4231_mixer(cs4231)) < 0)
+ err = snd_wss_mixer(wss);
+ if (err < 0)
goto _err;
- if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0)
+ err = snd_wss_timer(wss, 2, NULL);
+ if (err < 0)
goto _err;
if (pcm_channels[dev] > 0) {
if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)
goto _err;
}
- if ((err = snd_gusmax_mixer(cs4231)) < 0)
+ err = snd_gusmax_mixer(wss);
+ if (err < 0)
goto _err;
- if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0)
+ err = snd_gf1_rawmidi_new(gus, 0, NULL);
+ if (err < 0)
goto _err;
sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1);
@@ -336,11 +339,12 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
snd_card_set_dev(card, pdev);
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
goto _err;
maxcard->gus = gus;
- maxcard->cs4231 = cs4231;
+ maxcard->wss = wss;
dev_set_drvdata(pdev, card);
return 0;
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index ca0d7ace0c7..5faecfb602d 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -32,7 +32,7 @@
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#ifdef SNDRV_STB
#include <sound/tea6330t.h>
#endif
@@ -118,7 +118,7 @@ struct snd_interwave {
int irq;
struct snd_card *card;
struct snd_gus_card *gus;
- struct snd_cs4231 *cs4231;
+ struct snd_wss *wss;
#ifdef SNDRV_STB
struct resource *i2c_res;
#endif
@@ -312,7 +312,7 @@ static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id)
}
if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */
handled = 1;
- snd_cs4231_interrupt(irq, iwcard->cs4231);
+ snd_wss_interrupt(irq, iwcard->wss);
loop++;
}
} while (loop && --max > 0);
@@ -498,13 +498,17 @@ static void __devinit snd_interwave_init(int dev, struct snd_gus_card * gus)
}
static struct snd_kcontrol_new snd_interwave_controls[] = {
-CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
+WSS_DOUBLE("Master Playback Switch", 0,
+ CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Master Playback Volume", 0,
+ CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Mic Playback Switch", 0,
+ CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Mic Playback Volume", 0,
+ CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
};
-static int __devinit snd_interwave_mixer(struct snd_cs4231 *chip)
+static int __devinit snd_interwave_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -527,10 +531,10 @@ static int __devinit snd_interwave_mixer(struct snd_cs4231 *chip)
for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++)
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0)
return err;
- snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
- snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
- snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
- snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
+ snd_wss_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
+ snd_wss_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
+ snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
+ snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
/* reassign AUXA to SYNTHESIZER */
strcpy(id1.name, "Aux Playback Switch");
strcpy(id2.name, "Synth Playback Switch");
@@ -642,7 +646,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
{
int xirq, xdma1, xdma2;
struct snd_interwave *iwcard = card->private_data;
- struct snd_cs4231 *cs4231;
+ struct snd_wss *wss;
struct snd_gus_card *gus;
#ifdef SNDRV_STB
struct snd_i2c_bus *i2c_bus;
@@ -684,33 +688,39 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
}
iwcard->irq = xirq;
- if ((err = snd_cs4231_create(card,
- gus->gf1.port + 0x10c, -1, xirq,
- xdma2 < 0 ? xdma1 : xdma2, xdma1,
- CS4231_HW_INTERWAVE,
- CS4231_HWSHARE_IRQ |
- CS4231_HWSHARE_DMA1 |
- CS4231_HWSHARE_DMA2,
- &cs4231)) < 0)
+ err = snd_wss_create(card,
+ gus->gf1.port + 0x10c, -1, xirq,
+ xdma2 < 0 ? xdma1 : xdma2, xdma1,
+ WSS_HW_INTERWAVE,
+ WSS_HWSHARE_IRQ |
+ WSS_HWSHARE_DMA1 |
+ WSS_HWSHARE_DMA2,
+ &wss);
+ if (err < 0)
return err;
- if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0)
+ err = snd_wss_pcm(wss, 0, &pcm);
+ if (err < 0)
return err;
sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
strcat(pcm->name, " (codec)");
- if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0)
+ err = snd_wss_timer(wss, 2, NULL);
+ if (err < 0)
return err;
- if ((err = snd_cs4231_mixer(cs4231)) < 0)
+ err = snd_wss_mixer(wss);
+ if (err < 0)
return err;
if (pcm_channels[dev] > 0) {
- if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)
+ err = snd_gf1_pcm_new(gus, 1, 1, NULL);
+ if (err < 0)
return err;
}
- if ((err = snd_interwave_mixer(cs4231)) < 0)
+ err = snd_interwave_mixer(wss);
+ if (err < 0)
return err;
#ifdef SNDRV_STB
@@ -754,10 +764,11 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
if (xdma2 >= 0)
sprintf(card->longname + strlen(card->longname), "&%d", xdma2);
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- iwcard->cs4231 = cs4231;
+ iwcard->wss = wss;
iwcard->gus = gus;
return 0;
}
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 854a9f74b46..58c972b2af0 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -28,7 +28,7 @@
#include <linux/pnp.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
@@ -124,7 +124,6 @@ static int pnpc_registered;
#define OPL3SA2_PM_D3 (OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX)
struct snd_opl3sa2 {
- struct snd_card *card;
int version; /* 2 or 3 */
unsigned long port; /* control port */
struct resource *res_port; /* control port resource */
@@ -133,7 +132,7 @@ struct snd_opl3sa2 {
spinlock_t reg_lock;
struct snd_hwdep *synth;
struct snd_rawmidi *rmidi;
- struct snd_cs4231 *cs4231;
+ struct snd_wss *wss;
unsigned char ctlregs[0x20];
int ymode; /* SL added */
struct snd_kcontrol *master_switch;
@@ -222,14 +221,13 @@ static void snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, unsig
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip)
+static int __devinit snd_opl3sa2_detect(struct snd_card *card)
{
- struct snd_card *card;
+ struct snd_opl3sa2 *chip = card->private_data;
unsigned long port;
unsigned char tmp, tmp1;
char str[2];
- card = chip->card;
port = chip->port;
if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) {
snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
@@ -298,12 +296,14 @@ static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip)
static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
{
unsigned short status;
- struct snd_opl3sa2 *chip = dev_id;
+ struct snd_card *card = dev_id;
+ struct snd_opl3sa2 *chip;
int handled = 0;
- if (chip == NULL || chip->card == NULL)
+ if (card == NULL)
return IRQ_NONE;
+ chip = card->private_data;
status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS);
if (status & 0x20) {
@@ -318,7 +318,7 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
if (status & 0x07) { /* TI,CI,PI */
handled = 1;
- snd_cs4231_interrupt(irq, chip->cs4231);
+ snd_wss_interrupt(irq, chip->wss);
}
if (status & 0x40) { /* hardware volume change */
@@ -327,8 +327,10 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT);
snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT);
if (chip->master_switch && chip->master_volume) {
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_switch->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
}
}
return IRQ_RETVAL(handled);
@@ -336,29 +338,18 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
#define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_opl3sa2_info_single, \
+ .info = snd_wss_info_single, \
.get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
#define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
- .info = snd_opl3sa2_info_single, \
+ .info = snd_wss_info_single, \
.get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
.tlv = { .p = (xtlv) } }
-static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
@@ -402,29 +393,18 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
#define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_opl3sa2_info_double, \
+ .info = snd_wss_info_double, \
.get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
#define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
- .info = snd_opl3sa2_info_double, \
+ .info = snd_wss_info_double, \
.get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
.tlv = { .p = (xtlv) } }
-static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 24) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
@@ -512,9 +492,9 @@ static void snd_opl3sa2_master_free(struct snd_kcontrol *kcontrol)
chip->master_volume = NULL;
}
-static int __devinit snd_opl3sa2_mixer(struct snd_opl3sa2 *chip)
+static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
{
- struct snd_card *card = chip->card;
+ struct snd_opl3sa2 *chip = card->private_data;
struct snd_ctl_elem_id id1, id2;
struct snd_kcontrol *kctl;
unsigned int idx;
@@ -573,7 +553,7 @@ static int snd_opl3sa2_suspend(struct snd_card *card, pm_message_t state)
struct snd_opl3sa2 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- chip->cs4231->suspend(chip->cs4231);
+ chip->wss->suspend(chip->wss);
/* power down */
snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3);
@@ -597,8 +577,8 @@ static int snd_opl3sa2_resume(struct snd_card *card)
for (i = 0x12; i <= 0x16; i++)
snd_opl3sa2_write(chip, i, chip->ctlregs[i]);
}
- /* restore cs4231 */
- chip->cs4231->resume(chip->cs4231);
+ /* restore wss */
+ chip->wss->resume(chip->wss);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
@@ -650,7 +630,6 @@ static struct snd_card *snd_opl3sa2_card_new(int dev)
chip = card->private_data;
spin_lock_init(&chip->reg_lock);
chip->irq = -1;
- chip->card = card;
card->private_free = snd_opl3sa2_free;
return card;
}
@@ -659,7 +638,7 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
{
int xirq, xdma1, xdma2;
struct snd_opl3sa2 *chip;
- struct snd_cs4231 *cs4231;
+ struct snd_wss *wss;
struct snd_opl3 *opl3;
int err;
@@ -672,30 +651,36 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
xdma2 = dma2[dev];
if (xdma2 < 0)
chip->single_dma = 1;
- if ((err = snd_opl3sa2_detect(chip)) < 0)
+ err = snd_opl3sa2_detect(card);
+ if (err < 0)
return err;
- if (request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED, "OPL3-SA2", chip)) {
+ err = request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED,
+ "OPL3-SA2", card);
+ if (err) {
snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
return -ENODEV;
}
chip->irq = xirq;
- if ((err = snd_cs4231_create(card,
- wss_port[dev] + 4, -1,
- xirq, xdma1, xdma2,
- CS4231_HW_OPL3SA2,
- CS4231_HWSHARE_IRQ,
- &cs4231)) < 0) {
+ err = snd_wss_create(card,
+ wss_port[dev] + 4, -1,
+ xirq, xdma1, xdma2,
+ WSS_HW_OPL3SA2, WSS_HWSHARE_IRQ, &wss);
+ if (err < 0) {
snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
return err;
}
- chip->cs4231 = cs4231;
- if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0)
+ chip->wss = wss;
+ err = snd_wss_pcm(wss, 0, NULL);
+ if (err < 0)
return err;
- if ((err = snd_cs4231_mixer(cs4231)) < 0)
+ err = snd_wss_mixer(wss);
+ if (err < 0)
return err;
- if ((err = snd_opl3sa2_mixer(chip)) < 0)
+ err = snd_opl3sa2_mixer(card);
+ if (err < 0)
return err;
- if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0)
+ err = snd_wss_timer(wss, 0, NULL);
+ if (err < 0)
return err;
if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) {
if ((err = snd_opl3_create(card, fm_port[dev],
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 2a1e2f5d12c..440755cc001 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -32,7 +32,7 @@
#include <asm/io.h>
#include <asm/dma.h>
#include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/opl4.h>
#include <sound/control.h>
@@ -675,7 +675,8 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
unsigned int idx;
int err;
- snd_assert(miro != NULL && miro->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!miro || !miro->card))
+ return -EINVAL;
card = miro->card;
@@ -1221,7 +1222,7 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
int error;
struct snd_miro *miro;
- struct snd_cs4231 *codec;
+ struct snd_wss *codec;
struct snd_timer *timer;
struct snd_card *card;
struct snd_pcm *pcm;
@@ -1310,29 +1311,32 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
}
}
- if ((error = snd_miro_configure(miro))) {
+ error = snd_miro_configure(miro);
+ if (error) {
snd_card_free(card);
return error;
}
- if ((error = snd_cs4231_create(card, miro->wss_base + 4, -1,
- miro->irq, miro->dma1, miro->dma2,
- CS4231_HW_AD1845,
- 0,
- &codec)) < 0) {
+ error = snd_wss_create(card, miro->wss_base + 4, -1,
+ miro->irq, miro->dma1, miro->dma2,
+ WSS_HW_AD1845, 0, &codec);
+ if (error < 0) {
snd_card_free(card);
return error;
}
- if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) {
+ error = snd_wss_pcm(codec, 0, &pcm);
+ if (error < 0) {
snd_card_free(card);
return error;
}
- if ((error = snd_cs4231_mixer(codec)) < 0) {
+ error = snd_wss_mixer(codec);
+ if (error < 0) {
snd_card_free(card);
return error;
}
- if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) {
+ error = snd_wss_timer(codec, 0, &timer);
+ if (error < 0) {
snd_card_free(card);
return error;
}
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index fe1afc13a01..19706b0d849 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -33,16 +33,7 @@
#include <asm/io.h>
#include <asm/dma.h>
#include <sound/core.h>
-#ifdef CS4231
-#include <sound/cs4231.h>
-#else
-#ifndef OPTi93X
-#include <sound/ad1848.h>
-#else
-#include <sound/control.h>
-#include <sound/pcm.h>
-#endif /* OPTi93X */
-#endif /* CS4231 */
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#ifndef OPTi93X
@@ -73,7 +64,9 @@ MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+#ifdef CONFIG_PNP
static int isapnp = 1; /* Enable ISA PnP detection */
+#endif
static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */
static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */
static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */
@@ -90,8 +83,10 @@ module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
//module_param(enable, bool, 0444);
//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
+#ifdef CONFIG_PNP
module_param(isapnp, bool, 0444);
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
+#endif
module_param(port, long, 0444);
MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
module_param(mpu_port, long, 0444);
@@ -109,7 +104,6 @@ module_param(dma2, int, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif /* CS4231 || OPTi93X */
-#define OPTi9XX_HW_DETECT 0
#define OPTi9XX_HW_82C928 1
#define OPTi9XX_HW_82C929 2
#define OPTi9XX_HW_82C924 3
@@ -123,105 +117,12 @@ MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#ifdef OPTi93X
-#define OPTi93X_INDEX 0x00
-#define OPTi93X_DATA 0x01
#define OPTi93X_STATUS 0x02
-#define OPTi93X_DDATA 0x03
#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r)
-#define OPTi93X_MIXOUT_LEFT 0x00
-#define OPTi93X_MIXOUT_RIGHT 0x01
-#define OPTi93X_CD_LEFT_INPUT 0x02
-#define OPTi93X_CD_RIGHT_INPUT 0x03
-#define OPTi930_AUX_LEFT_INPUT 0x04
-#define OPTi930_AUX_RIGHT_INPUT 0x05
-#define OPTi931_FM_LEFT_INPUT 0x04
-#define OPTi931_FM_RIGHT_INPUT 0x05
-#define OPTi93X_DAC_LEFT 0x06
-#define OPTi93X_DAC_RIGHT 0x07
-#define OPTi93X_PLAY_FORMAT 0x08
-#define OPTi93X_IFACE_CONF 0x09
-#define OPTi93X_PIN_CTRL 0x0a
-#define OPTi93X_ERR_INIT 0x0b
-#define OPTi93X_ID 0x0c
-#define OPTi93X_PLAY_UPR_CNT 0x0e
-#define OPTi93X_PLAY_LWR_CNT 0x0f
-#define OPTi931_AUX_LEFT_INPUT 0x10
-#define OPTi931_AUX_RIGHT_INPUT 0x11
-#define OPTi93X_LINE_LEFT_INPUT 0x12
-#define OPTi93X_LINE_RIGHT_INPUT 0x13
-#define OPTi93X_MIC_LEFT_INPUT 0x14
-#define OPTi93X_MIC_RIGHT_INPUT 0x15
-#define OPTi93X_OUT_LEFT 0x16
-#define OPTi93X_OUT_RIGHT 0x17
-#define OPTi93X_CAPT_FORMAT 0x1c
-#define OPTi93X_CAPT_UPR_CNT 0x1e
-#define OPTi93X_CAPT_LWR_CNT 0x1f
-
-#define OPTi93X_TRD 0x20
-#define OPTi93X_MCE 0x40
-#define OPTi93X_INIT 0x80
-
-#define OPTi93X_MIXOUT_MIC_GAIN 0x20
-#define OPTi93X_MIXOUT_LINE 0x00
-#define OPTi93X_MIXOUT_CD 0x40
-#define OPTi93X_MIXOUT_MIC 0x80
-#define OPTi93X_MIXOUT_MIXER 0xc0
-
-#define OPTi93X_STEREO 0x10
-#define OPTi93X_LINEAR_8 0x00
-#define OPTi93X_ULAW_8 0x20
-#define OPTi93X_LINEAR_16_LIT 0x40
-#define OPTi93X_ALAW_8 0x60
-#define OPTi93X_ADPCM_16 0xa0
-#define OPTi93X_LINEAR_16_BIG 0xc0
-
-#define OPTi93X_CAPTURE_PIO 0x80
-#define OPTi93X_PLAYBACK_PIO 0x40
-#define OPTi93X_AUTOCALIB 0x08
-#define OPTi93X_SINGLE_DMA 0x04
-#define OPTi93X_CAPTURE_ENABLE 0x02
-#define OPTi93X_PLAYBACK_ENABLE 0x01
-
-#define OPTi93X_IRQ_ENABLE 0x02
-
-#define OPTi93X_DMA_REQUEST 0x10
-#define OPTi93X_CALIB_IN_PROGRESS 0x20
-
#define OPTi93X_IRQ_PLAYBACK 0x04
#define OPTi93X_IRQ_CAPTURE 0x08
-
-struct snd_opti93x {
- unsigned long port;
- struct resource *res_port;
- int irq;
- int dma1;
- int dma2;
-
- struct snd_opti9xx *chip;
- unsigned short hardware;
- unsigned char image[32];
-
- unsigned char mce_bit;
- unsigned short mode;
- int mute;
-
- spinlock_t lock;
-
- struct snd_card *card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *playback_substream;
- struct snd_pcm_substream *capture_substream;
- unsigned int p_dma_size;
- unsigned int c_dma_size;
-};
-
-#define OPTi93X_MODE_NONE 0x00
-#define OPTi93X_MODE_PLAY 0x01
-#define OPTi93X_MODE_CAPTURE 0x02
-#define OPTi93X_MODE_OPEN (OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE)
-
#endif /* OPTi93X */
struct snd_opti9xx {
@@ -234,6 +135,7 @@ struct snd_opti9xx {
unsigned long mc_base_size;
#ifdef OPTi93X
unsigned long mc_indir_index;
+ struct snd_wss *codec;
#endif /* OPTi93X */
unsigned long pwd_reg;
@@ -242,9 +144,7 @@ struct snd_opti9xx {
long wss_base;
int irq;
int dma1;
-#if defined(CS4231) || defined(OPTi93X)
int dma2;
-#endif /* CS4231 || OPTi93X */
long fm_port;
@@ -319,9 +219,7 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
chip->wss_base = -1;
chip->irq = -1;
chip->dma1 = -1;
-#if defined(CS4231) || defined (OPTi93X)
chip->dma2 = -1;
-#endif /* CS4231 || OPTi93X */
chip->fm_port = -1;
chip->mpu_port = -1;
chip->mpu_irq = -1;
@@ -491,16 +389,9 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
break;
#else /* OPTi93X */
- case OPTi9XX_HW_82C930:
case OPTi9XX_HW_82C931:
case OPTi9XX_HW_82C933:
- snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
- snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
- snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
- (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
- 0x34);
- snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
- /*
+ /*
* The BTC 1817DW has QS1000 wavetable which is connected
* to the serial digital input of the OPTI931.
*/
@@ -510,6 +401,13 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
* or digital input signal.
*/
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
+ case OPTi9XX_HW_82C930: /* FALL THROUGH */
+ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
+ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
+ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
+ (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
+ 0x34);
+ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
break;
#endif /* OPTi93X */
@@ -654,979 +552,23 @@ __skip_mpu:
#ifdef OPTi93X
-static unsigned char snd_opti93x_default_image[32] =
-{
- 0x00, /* 00/00 - l_mixout_outctrl */
- 0x00, /* 01/01 - r_mixout_outctrl */
- 0x88, /* 02/02 - l_cd_inctrl */
- 0x88, /* 03/03 - r_cd_inctrl */
- 0x88, /* 04/04 - l_a1/fm_inctrl */
- 0x88, /* 05/05 - r_a1/fm_inctrl */
- 0x80, /* 06/06 - l_dac_inctrl */
- 0x80, /* 07/07 - r_dac_inctrl */
- 0x00, /* 08/08 - ply_dataform_reg */
- 0x00, /* 09/09 - if_conf */
- 0x00, /* 0a/10 - pin_ctrl */
- 0x00, /* 0b/11 - err_init_reg */
- 0x0a, /* 0c/12 - id_reg */
- 0x00, /* 0d/13 - reserved */
- 0x00, /* 0e/14 - ply_upcount_reg */
- 0x00, /* 0f/15 - ply_lowcount_reg */
- 0x88, /* 10/16 - reserved/l_a1_inctrl */
- 0x88, /* 11/17 - reserved/r_a1_inctrl */
- 0x88, /* 12/18 - l_line_inctrl */
- 0x88, /* 13/19 - r_line_inctrl */
- 0x88, /* 14/20 - l_mic_inctrl */
- 0x88, /* 15/21 - r_mic_inctrl */
- 0x80, /* 16/22 - l_out_outctrl */
- 0x80, /* 17/23 - r_out_outctrl */
- 0x00, /* 18/24 - reserved */
- 0x00, /* 19/25 - reserved */
- 0x00, /* 1a/26 - reserved */
- 0x00, /* 1b/27 - reserved */
- 0x00, /* 1c/28 - cap_dataform_reg */
- 0x00, /* 1d/29 - reserved */
- 0x00, /* 1e/30 - cap_upcount_reg */
- 0x00 /* 1f/31 - cap_lowcount_reg */
-};
-
-
-static int snd_opti93x_busy_wait(struct snd_opti93x *chip)
-{
- int timeout;
-
- for (timeout = 250; timeout-- > 0; udelay(10))
- if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT))
- return 0;
-
- snd_printk("chip still busy.\n");
- return -EBUSY;
-}
-
-static unsigned char snd_opti93x_in(struct snd_opti93x *chip, unsigned char reg)
-{
- snd_opti93x_busy_wait(chip);
- outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
- return inb(OPTi93X_PORT(chip, DATA));
-}
-
-static void snd_opti93x_out(struct snd_opti93x *chip, unsigned char reg,
- unsigned char value)
-{
- snd_opti93x_busy_wait(chip);
- outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
- outb(value, OPTi93X_PORT(chip, DATA));
-}
-
-static void snd_opti93x_out_image(struct snd_opti93x *chip, unsigned char reg,
- unsigned char value)
-{
- snd_opti93x_out(chip, reg, chip->image[reg] = value);
-}
-
-static void snd_opti93x_out_mask(struct snd_opti93x *chip, unsigned char reg,
- unsigned char mask, unsigned char value)
-{
- snd_opti93x_out_image(chip, reg,
- (chip->image[reg] & ~mask) | (value & mask));
-}
-
-
-static void snd_opti93x_mce_up(struct snd_opti93x *chip)
-{
- snd_opti93x_busy_wait(chip);
-
- chip->mce_bit = OPTi93X_MCE;
- if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE))
- outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
-}
-
-static void snd_opti93x_mce_down(struct snd_opti93x *chip)
-{
- snd_opti93x_busy_wait(chip);
-
- chip->mce_bit = 0;
- if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)
- outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
-}
-
-#define snd_opti93x_mute_reg(chip, reg, mute) \
- snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]);
-
-static void snd_opti93x_mute(struct snd_opti93x *chip, int mute)
-{
- mute = mute ? 1 : 0;
- if (chip->mute == mute)
- return;
-
- chip->mute = mute;
-
- snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute);
- switch (chip->hardware) {
- case OPTi9XX_HW_82C930:
- snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute);
- break;
- case OPTi9XX_HW_82C931:
- case OPTi9XX_HW_82C933:
- snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute);
- }
- snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute);
- snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute);
-}
-
-
-static unsigned int snd_opti93x_get_count(unsigned char format,
- unsigned int size)
-{
- switch (format & 0xe0) {
- case OPTi93X_LINEAR_16_LIT:
- case OPTi93X_LINEAR_16_BIG:
- size >>= 1;
- break;
- case OPTi93X_ADPCM_16:
- return size >> 2;
- }
- return (format & OPTi93X_STEREO) ? (size >> 1) : size;
-}
-
-static unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000,
- 18900, 22050, 27428, 32000, 33075, 37800,
- 44100, 48000 };
-#define RATES ARRAY_SIZE(rates)
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = RATES,
- .list = rates,
- .mask = 0,
-};
-
-static unsigned char bits[] = { 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02,
- 0x05, 0x07, 0x04, 0x06, 0x0d, 0x09,
- 0x0b, 0x0c};
-
-static unsigned char snd_opti93x_get_freq(unsigned int rate)
-{
- unsigned int i;
-
- for (i = 0; i < RATES; i++) {
- if (rate == rates[i])
- return bits[i];
- }
- snd_BUG();
- return bits[RATES-1];
-}
-
-static unsigned char snd_opti93x_get_format(struct snd_opti93x *chip,
- unsigned int format, int channels)
-{
- unsigned char retval = OPTi93X_LINEAR_8;
-
- switch (format) {
- case SNDRV_PCM_FORMAT_MU_LAW:
- retval = OPTi93X_ULAW_8;
- break;
- case SNDRV_PCM_FORMAT_A_LAW:
- retval = OPTi93X_ALAW_8;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- retval = OPTi93X_LINEAR_16_LIT;
- break;
- case SNDRV_PCM_FORMAT_S16_BE:
- retval = OPTi93X_LINEAR_16_BIG;
- break;
- case SNDRV_PCM_FORMAT_IMA_ADPCM:
- retval = OPTi93X_ADPCM_16;
- }
- return (channels > 1) ? (retval | OPTi93X_STEREO) : retval;
-}
-
-
-static void snd_opti93x_playback_format(struct snd_opti93x *chip, unsigned char fmt)
-{
- unsigned char mask;
-
- snd_opti93x_mute(chip, 1);
-
- snd_opti93x_mce_up(chip);
- mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff;
- snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt);
- snd_opti93x_mce_down(chip);
-
- snd_opti93x_mute(chip, 0);
-}
-
-static void snd_opti93x_capture_format(struct snd_opti93x *chip, unsigned char fmt)
-{
- snd_opti93x_mute(chip, 1);
-
- snd_opti93x_mce_up(chip);
- if (!(chip->mode & OPTi93X_MODE_PLAY))
- snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt);
- else
- fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0;
- snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt);
- snd_opti93x_mce_down(chip);
-
- snd_opti93x_mute(chip, 0);
-}
-
-
-static int snd_opti93x_open(struct snd_opti93x *chip, unsigned int mode)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
-
- if (chip->mode & mode) {
- spin_unlock_irqrestore(&chip->lock, flags);
- return -EAGAIN;
- }
-
- if (!(chip->mode & OPTi93X_MODE_OPEN)) {
- outb(0x00, OPTi93X_PORT(chip, STATUS));
- snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL,
- OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE);
- chip->mode = mode;
- }
- else
- chip->mode |= mode;
-
- spin_unlock_irqrestore(&chip->lock, flags);
- return 0;
-}
-
-static void snd_opti93x_close(struct snd_opti93x *chip, unsigned int mode)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
-
- chip->mode &= ~mode;
- if (chip->mode & OPTi93X_MODE_OPEN) {
- spin_unlock_irqrestore(&chip->lock, flags);
- return;
- }
-
- snd_opti93x_mute(chip, 1);
-
- outb(0, OPTi93X_PORT(chip, STATUS));
- snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE,
- ~OPTi93X_IRQ_ENABLE);
-
- snd_opti93x_mce_up(chip);
- snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00);
- snd_opti93x_mce_down(chip);
- chip->mode = 0;
-
- snd_opti93x_mute(chip, 0);
- spin_unlock_irqrestore(&chip->lock, flags);
-}
-
-static int snd_opti93x_trigger(struct snd_pcm_substream *substream,
- unsigned char what, int cmd)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_STOP:
- {
- unsigned int what = 0;
- struct snd_pcm_substream *s;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s == chip->playback_substream) {
- what |= OPTi93X_PLAYBACK_ENABLE;
- snd_pcm_trigger_done(s, substream);
- } else if (s == chip->capture_substream) {
- what |= OPTi93X_CAPTURE_ENABLE;
- snd_pcm_trigger_done(s, substream);
- }
- }
- spin_lock(&chip->lock);
- if (cmd == SNDRV_PCM_TRIGGER_START) {
- snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
- if (what & OPTi93X_CAPTURE_ENABLE)
- udelay(50);
- } else
- snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00);
- spin_unlock(&chip->lock);
- break;
- }
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int snd_opti93x_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_opti93x_trigger(substream,
- OPTi93X_PLAYBACK_ENABLE, cmd);
-}
-
-static int snd_opti93x_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_opti93x_trigger(substream,
- OPTi93X_CAPTURE_ENABLE, cmd);
-}
-
-static int snd_opti93x_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-
-static int snd_opti93x_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-
-static int snd_opti93x_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- unsigned char format;
- unsigned int count = snd_pcm_lib_period_bytes(substream);
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-
- spin_lock_irqsave(&chip->lock, flags);
-
- chip->p_dma_size = size;
- snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
- OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO,
- ~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO));
-
- snd_dma_program(chip->dma1, runtime->dma_addr, size,
- DMA_MODE_WRITE | DMA_AUTOINIT);
-
- format = snd_opti93x_get_freq(runtime->rate);
- format |= snd_opti93x_get_format(chip, runtime->format,
- runtime->channels);
- snd_opti93x_playback_format(chip, format);
- format = chip->image[OPTi93X_PLAY_FORMAT];
-
- count = snd_opti93x_get_count(format, count) - 1;
- snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count);
- snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8);
-
- spin_unlock_irqrestore(&chip->lock, flags);
- return 0;
-}
-
-static int snd_opti93x_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- unsigned char format;
- unsigned int count = snd_pcm_lib_period_bytes(substream);
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-
- spin_lock_irqsave(&chip->lock, flags);
-
- chip->c_dma_size = size;
- snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
- OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, 0);
-
- snd_dma_program(chip->dma2, runtime->dma_addr, size,
- DMA_MODE_READ | DMA_AUTOINIT);
-
- format = snd_opti93x_get_freq(runtime->rate);
- format |= snd_opti93x_get_format(chip, runtime->format,
- runtime->channels);
- snd_opti93x_capture_format(chip, format);
- format = chip->image[OPTi93X_CAPT_FORMAT];
-
- count = snd_opti93x_get_count(format, count) - 1;
- snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count);
- snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8);
-
- spin_unlock_irqrestore(&chip->lock, flags);
- return 0;
-}
-
-static snd_pcm_uframes_t snd_opti93x_playback_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
- size_t ptr;
-
- if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE))
- return 0;
-
- ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
- return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_opti93x_capture_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
- size_t ptr;
-
- if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE))
- return 0;
-
- ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
- return bytes_to_frames(substream->runtime, ptr);
-}
-
-
-static void snd_opti93x_overrange(struct snd_opti93x *chip)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
-
- if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02))
- chip->capture_substream->runtime->overrange++;
-
- spin_unlock_irqrestore(&chip->lock, flags);
-}
-
static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
{
- struct snd_opti93x *codec = dev_id;
+ struct snd_wss *codec = dev_id;
+ struct snd_opti9xx *chip = codec->card->private_data;
unsigned char status;
- status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11));
+ status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
snd_pcm_period_elapsed(codec->playback_substream);
if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
- snd_opti93x_overrange(codec);
+ snd_wss_overrange(codec);
snd_pcm_period_elapsed(codec->capture_substream);
}
outb(0x00, OPTi93X_PORT(codec, STATUS));
return IRQ_HANDLED;
}
-
-static struct snd_pcm_hardware snd_opti93x_playback = {
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
- .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
- .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5512,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_opti93x_capture = {
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
- .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
- .rates = SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5512,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
-static int snd_opti93x_playback_open(struct snd_pcm_substream *substream)
-{
- int error;
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0)
- return error;
- snd_pcm_set_sync(substream);
- chip->playback_substream = substream;
- runtime->hw = snd_opti93x_playback;
- snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- return error;
-}
-
-static int snd_opti93x_capture_open(struct snd_pcm_substream *substream)
-{
- int error;
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0)
- return error;
- runtime->hw = snd_opti93x_capture;
- snd_pcm_set_sync(substream);
- chip->capture_substream = substream;
- snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- return error;
-}
-
-static int snd_opti93x_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
- chip->playback_substream = NULL;
- snd_opti93x_close(chip, OPTi93X_MODE_PLAY);
- return 0;
-}
-
-static int snd_opti93x_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
- chip->capture_substream = NULL;
- snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE);
- return 0;
-}
-
-
-static void snd_opti93x_init(struct snd_opti93x *chip)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&chip->lock, flags);
- snd_opti93x_mce_up(chip);
-
- for (i = 0; i < 32; i++)
- snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]);
-
- snd_opti93x_mce_down(chip);
- spin_unlock_irqrestore(&chip->lock, flags);
-}
-
-static int snd_opti93x_probe(struct snd_opti93x *chip)
-{
- unsigned long flags;
- unsigned char val;
-
- spin_lock_irqsave(&chip->lock, flags);
- val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f;
- spin_unlock_irqrestore(&chip->lock, flags);
-
- return (val == 0x0a) ? 0 : -ENODEV;
-}
-
-static int snd_opti93x_free(struct snd_opti93x *chip)
-{
- release_and_free_resource(chip->res_port);
- if (chip->dma1 >= 0) {
- disable_dma(chip->dma1);
- free_dma(chip->dma1);
- }
- if (chip->dma2 >= 0) {
- disable_dma(chip->dma2);
- free_dma(chip->dma2);
- }
- if (chip->irq >= 0) {
- free_irq(chip->irq, chip);
- }
- kfree(chip);
- return 0;
-}
-
-static int snd_opti93x_dev_free(struct snd_device *device)
-{
- struct snd_opti93x *chip = device->device_data;
- return snd_opti93x_free(chip);
-}
-
-static const char *snd_opti93x_chip_id(struct snd_opti93x *codec)
-{
- switch (codec->hardware) {
- case OPTi9XX_HW_82C930: return "82C930";
- case OPTi9XX_HW_82C931: return "82C931";
- case OPTi9XX_HW_82C933: return "82C933";
- default: return "???";
- }
-}
-
-static int snd_opti93x_create(struct snd_card *card, struct snd_opti9xx *chip,
- int dma1, int dma2,
- struct snd_opti93x **rcodec)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_opti93x_dev_free,
- };
- int error;
- struct snd_opti93x *codec;
-
- *rcodec = NULL;
- codec = kzalloc(sizeof(*codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
- codec->irq = -1;
- codec->dma1 = -1;
- codec->dma2 = -1;
-
- if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) {
- snd_printk(KERN_ERR "opti9xx: can't grab port 0x%lx\n", chip->wss_base + 4);
- snd_opti93x_free(codec);
- return -EBUSY;
- }
- if (request_dma(dma1, "OPTI93x - 1")) {
- snd_printk(KERN_ERR "opti9xx: can't grab DMA1 %d\n", dma1);
- snd_opti93x_free(codec);
- return -EBUSY;
- }
- codec->dma1 = chip->dma1;
- if (request_dma(dma2, "OPTI93x - 2")) {
- snd_printk(KERN_ERR "opti9xx: can't grab DMA2 %d\n", dma2);
- snd_opti93x_free(codec);
- return -EBUSY;
- }
- codec->dma2 = chip->dma2;
-
- if (request_irq(chip->irq, snd_opti93x_interrupt, IRQF_DISABLED, DEV_NAME" - WSS", codec)) {
- snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
- snd_opti93x_free(codec);
- return -EBUSY;
- }
-
- codec->card = card;
- codec->port = chip->wss_base + 4;
- codec->irq = chip->irq;
-
- spin_lock_init(&codec->lock);
- codec->hardware = chip->hardware;
- codec->chip = chip;
-
- if ((error = snd_opti93x_probe(codec))) {
- snd_opti93x_free(codec);
- return error;
- }
-
- snd_opti93x_init(codec);
-
- /* Register device */
- if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
- snd_opti93x_free(codec);
- return error;
- }
-
- *rcodec = codec;
- return 0;
-}
-
-static struct snd_pcm_ops snd_opti93x_playback_ops = {
- .open = snd_opti93x_playback_open,
- .close = snd_opti93x_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_opti93x_hw_params,
- .hw_free = snd_opti93x_hw_free,
- .prepare = snd_opti93x_playback_prepare,
- .trigger = snd_opti93x_playback_trigger,
- .pointer = snd_opti93x_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_opti93x_capture_ops = {
- .open = snd_opti93x_capture_open,
- .close = snd_opti93x_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_opti93x_hw_params,
- .hw_free = snd_opti93x_hw_free,
- .prepare = snd_opti93x_capture_prepare,
- .trigger = snd_opti93x_capture_trigger,
- .pointer = snd_opti93x_capture_pointer,
-};
-
-static int snd_opti93x_pcm(struct snd_opti93x *codec, int device, struct snd_pcm **rpcm)
-{
- int error;
- struct snd_pcm *pcm;
-
- if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm)) < 0)
- return error;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops);
-
- pcm->private_data = codec;
- pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
-
- strcpy(pcm->name, snd_opti93x_chip_id(codec));
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024);
-
- codec->pcm = pcm;
- if (rpcm)
- *rpcm = pcm;
- return 0;
-}
-
-/*
- * MIXER part
- */
-
-static int snd_opti93x_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[4] = {
- "Line1", "Aux", "Mic", "Mix"
- };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int snd_opti93x_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
- ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6;
- ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6;
- spin_unlock_irqrestore(&chip->lock, flags);
- return 0;
-}
-
-static int snd_opti93x_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- unsigned short left, right;
- int change;
-
- if (ucontrol->value.enumerated.item[0] > 3 ||
- ucontrol->value.enumerated.item[1] > 3)
- return -EINVAL;
- left = ucontrol->value.enumerated.item[0] << 6;
- right = ucontrol->value.enumerated.item[1] << 6;
- spin_lock_irqsave(&chip->lock, flags);
- left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left;
- right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right;
- change = left != chip->image[OPTi93X_MIXOUT_LEFT] ||
- right != chip->image[OPTi93X_MIXOUT_RIGHT];
- snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left);
- snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right);
- spin_unlock_irqrestore(&chip->lock, flags);
- return change;
-}
-
-#if 0
-
-#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_opti93x_info_single, \
- .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \
- .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
-
-static int snd_opti93x_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-static int snd_opti93x_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
-
- spin_lock_irqsave(&chip->lock, flags);
- ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->lock, flags);
- if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- return 0;
-}
-
-static int snd_opti93x_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
- int change;
- unsigned short val;
-
- val = (ucontrol->value.integer.value[0] & mask);
- if (invert)
- val = mask - val;
- val <<= shift;
- spin_lock_irqsave(&chip->lock, flags);
- val = (chip->image[reg] & ~(mask << shift)) | val;
- change = val != chip->image[reg];
- snd_opti93x_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->lock, flags);
- return change;
-}
-
-#endif /* single */
-
-#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_opti93x_info_double, \
- .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \
- .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
-
-#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
- do { xctl.private_value ^= 22; } while (0)
-#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \
- do { xctl.private_value &= ~0x0000ffff; \
- xctl.private_value |= left_reg | (right_reg << 8); } while (0)
-
-static int snd_opti93x_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 24) & 0xff;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-static int snd_opti93x_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int left_reg = kcontrol->private_value & 0xff;
- int right_reg = (kcontrol->private_value >> 8) & 0xff;
- int shift_left = (kcontrol->private_value >> 16) & 0x07;
- int shift_right = (kcontrol->private_value >> 19) & 0x07;
- int mask = (kcontrol->private_value >> 24) & 0xff;
- int invert = (kcontrol->private_value >> 22) & 1;
-
- spin_lock_irqsave(&chip->lock, flags);
- ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
- ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->lock, flags);
- if (invert) {
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
- }
- return 0;
-}
-
-static int snd_opti93x_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- int left_reg = kcontrol->private_value & 0xff;
- int right_reg = (kcontrol->private_value >> 8) & 0xff;
- int shift_left = (kcontrol->private_value >> 16) & 0x07;
- int shift_right = (kcontrol->private_value >> 19) & 0x07;
- int mask = (kcontrol->private_value >> 24) & 0xff;
- int invert = (kcontrol->private_value >> 22) & 1;
- int change;
- unsigned short val1, val2;
-
- val1 = ucontrol->value.integer.value[0] & mask;
- val2 = ucontrol->value.integer.value[1] & mask;
- if (invert) {
- val1 = mask - val1;
- val2 = mask - val2;
- }
- val1 <<= shift_left;
- val2 <<= shift_right;
- spin_lock_irqsave(&chip->lock, flags);
- val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
- val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
- change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
- snd_opti93x_out_image(chip, left_reg, val1);
- snd_opti93x_out_image(chip, right_reg, val2);
- spin_unlock_irqrestore(&chip->lock, flags);
- return change;
-}
-
-static struct snd_kcontrol_new snd_opti93x_controls[] __devinitdata = {
-OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
-OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1),
-OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1),
-OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = snd_opti93x_info_mux,
- .get = snd_opti93x_get_mux,
- .put = snd_opti93x_put_mux,
-}
-};
-
-static int __devinit snd_opti93x_mixer(struct snd_opti93x *chip)
-{
- struct snd_card *card;
- struct snd_kcontrol_new knew;
- int err;
- unsigned int idx;
-
- snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
-
- card = chip->card;
-
- strcpy(card->mixername, snd_opti93x_chip_id(chip));
-
- for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
- knew = snd_opti93x_controls[idx];
- if (chip->hardware == OPTi9XX_HW_82C930) {
- if (strstr(knew.name, "FM")) /* skip FM controls */
- continue;
- else if (strcmp(knew.name, "Mic Playback Volume"))
- OPTi93X_DOUBLE_INVERT_INVERT(knew);
- else if (strstr(knew.name, "Aux"))
- OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT);
- else if (strcmp(knew.name, "PCM Playback Volume"))
- OPTi93X_DOUBLE_INVERT_INVERT(knew);
- else if (strcmp(knew.name, "Master Playback Volume"))
- OPTi93X_DOUBLE_INVERT_INVERT(knew);
- }
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0)
- return err;
- }
- return 0;
-}
-
#endif /* OPTi93X */
static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
@@ -1739,8 +681,16 @@ static void snd_card_opti9xx_free(struct snd_card *card)
{
struct snd_opti9xx *chip = card->private_data;
- if (chip)
+ if (chip) {
+#ifdef OPTi93X
+ struct snd_wss *codec = chip->codec;
+ if (codec && codec->irq > 0) {
+ disable_irq(codec->irq);
+ free_irq(codec->irq, codec);
+ }
+#endif
release_and_free_resource(chip->res_mc_base);
+ }
}
static int __devinit snd_opti9xx_probe(struct snd_card *card)
@@ -1748,13 +698,9 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
int error;
struct snd_opti9xx *chip = card->private_data;
-#if defined(OPTi93X)
- struct snd_opti93x *codec;
-#elif defined(CS4231)
- struct snd_cs4231 *codec;
+ struct snd_wss *codec;
+#ifdef CS4231
struct snd_timer *timer;
-#else
- struct snd_ad1848 *codec;
#endif
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
@@ -1773,46 +719,52 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
chip->dma1 = dma1;
#if defined(CS4231) || defined(OPTi93X)
chip->dma2 = dma2;
+#else
+ chip->dma2 = -1;
#endif
if (chip->wss_base == SNDRV_AUTO_PORT) {
- if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
+ chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
+ if (chip->wss_base < 0) {
snd_printk("unable to find a free WSS port\n");
return -EBUSY;
}
}
- if ((error = snd_opti9xx_configure(chip)))
+ error = snd_opti9xx_configure(chip);
+ if (error)
return error;
-#if defined(OPTi93X)
- if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec)))
- return error;
- if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0)
- return error;
- if ((error = snd_opti93x_mixer(codec)) < 0)
- return error;
-#elif defined(CS4231)
- if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
- chip->irq, chip->dma1, chip->dma2,
- CS4231_HW_DETECT,
- 0,
- &codec)) < 0)
- return error;
- if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0)
+ error = snd_wss_create(card, chip->wss_base + 4, -1,
+ chip->irq, chip->dma1, chip->dma2,
+#ifdef OPTi93X
+ WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
+#else
+ WSS_HW_DETECT, 0,
+#endif
+ &codec);
+ if (error < 0)
return error;
- if ((error = snd_cs4231_mixer(codec)) < 0)
+#ifdef OPTi93X
+ chip->codec = codec;
+#endif
+ error = snd_wss_pcm(codec, 0, &pcm);
+ if (error < 0)
return error;
- if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0)
+ error = snd_wss_mixer(codec);
+ if (error < 0)
return error;
-#else
- if ((error = snd_ad1848_create(card, chip->wss_base + 4,
- chip->irq, chip->dma1,
- AD1848_HW_DETECT, &codec)) < 0)
- return error;
- if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0)
+#ifdef CS4231
+ error = snd_wss_timer(codec, 0, &timer);
+ if (error < 0)
return error;
- if ((error = snd_ad1848_mixer(codec)) < 0)
+#endif
+#ifdef OPTi93X
+ error = request_irq(chip->irq, snd_opti93x_interrupt,
+ IRQF_DISABLED, DEV_NAME" - WSS", codec);
+ if (error < 0) {
+ snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
return error;
+ }
#endif
strcpy(card->driver, chip->name);
sprintf(card->shortname, "OPTi %s", card->driver);
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index c9d1c986d70..1098a56b2f4 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -34,5 +34,3 @@ ifeq ($(CONFIG_SND_SB16_CSP),y)
obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
endif
obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
-
-obj-m := $(sort $(obj-m))
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index b35be7d9a9f..96678d5d383 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1023,7 +1023,8 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
{
int i, err = 0;
- snd_assert(emu != NULL && card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!emu || !card))
+ return -EINVAL;
spin_lock_init(&emu->control_lock);
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index 1be16c9700f..c99c6078be3 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -156,7 +156,8 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
struct snd_emu8000 *emu;
emu = rec->hw;
- snd_assert(sp != NULL, return -EINVAL);
+ if (snd_BUG_ON(!sp))
+ return -EINVAL;
if (sp->v.size == 0)
return 0;
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index f3fd7b4f466..49037d074c7 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -35,13 +35,11 @@
MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor");
MODULE_LICENSE("GPL");
-#ifndef CONFIG_SND_SB16_CSP_FIRMWARE_IN_KERNEL
MODULE_FIRMWARE("sb16/mulaw_main.csp");
MODULE_FIRMWARE("sb16/alaw_main.csp");
MODULE_FIRMWARE("sb16/ima_adpcm_init.csp");
MODULE_FIRMWARE("sb16/ima_adpcm_playback.csp");
MODULE_FIRMWARE("sb16/ima_adpcm_capture.csp");
-#endif
#ifdef SNDRV_LITTLE_ENDIAN
#define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
@@ -168,17 +166,13 @@ int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
*/
static void snd_sb_csp_free(struct snd_hwdep *hwdep)
{
-#ifndef CONFIG_SND_SB16_CSP_FIRMWARE_IN_KERNEL
int i;
-#endif
struct snd_sb_csp *p = hwdep->private_data;
if (p) {
if (p->running & SNDRV_SB_CSP_ST_RUNNING)
snd_sb_csp_stop(p);
-#ifndef CONFIG_SND_SB16_CSP_FIRMWARE_IN_KERNEL
for (i = 0; i < ARRAY_SIZE(p->csp_programs); ++i)
release_firmware(p->csp_programs[i]);
-#endif
kfree(p);
}
}
@@ -204,7 +198,8 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i
struct snd_sb_csp_start start_info;
int err;
- snd_assert(p != NULL, return -EINVAL);
+ if (snd_BUG_ON(!p))
+ return -EINVAL;
if (snd_sb_csp_check_version(p))
return -ENODEV;
@@ -701,18 +696,6 @@ static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __use
return err;
}
-#ifdef CONFIG_SND_SB16_CSP_FIRMWARE_IN_KERNEL
-#include "sb16_csp_codecs.h"
-
-static const struct firmware snd_sb_csp_static_programs[] = {
- { .data = mulaw_main, .size = sizeof mulaw_main },
- { .data = alaw_main, .size = sizeof alaw_main },
- { .data = ima_adpcm_init, .size = sizeof ima_adpcm_init },
- { .data = ima_adpcm_playback, .size = sizeof ima_adpcm_playback },
- { .data = ima_adpcm_capture, .size = sizeof ima_adpcm_capture },
-};
-#endif
-
static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags)
{
static const char *const names[] = {
@@ -727,14 +710,10 @@ static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags)
BUILD_BUG_ON(ARRAY_SIZE(names) != CSP_PROGRAM_COUNT);
program = p->csp_programs[index];
if (!program) {
-#ifdef CONFIG_SND_SB16_CSP_FIRMWARE_IN_KERNEL
- program = &snd_sb_csp_static_programs[index];
-#else
int err = request_firmware(&program, names[index],
p->chip->card->dev);
if (err < 0)
return err;
-#endif
p->csp_programs[index] = program;
}
return snd_sb_csp_load(p, program->data, program->size, flags);
@@ -1068,7 +1047,8 @@ static int snd_sb_qsound_build(struct snd_sb_csp * p)
struct snd_card *card;
int err;
- snd_assert(p != NULL, return -EINVAL);
+ if (snd_BUG_ON(!p))
+ return -EINVAL;
card = p->chip->card;
p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2;
@@ -1093,7 +1073,8 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
struct snd_card *card;
unsigned long flags;
- snd_assert(p != NULL, return);
+ if (snd_BUG_ON(!p))
+ return;
card = p->chip->card;
diff --git a/sound/isa/sb/sb16_csp_codecs.h b/sound/isa/sb/sb16_csp_codecs.h
deleted file mode 100644
index f0e8b0dcb57..00000000000
--- a/sound/isa/sb/sb16_csp_codecs.h
+++ /dev/null
@@ -1,949 +0,0 @@
-/*
- * Copyright (c) 1994 Creative Technology Ltd.
- * Microcode files for SB16 Advanced Signal Processor
- *
- * 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
- *
- */
-
-static unsigned char mulaw_main[] = {
- 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
- 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
- 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
- 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
- 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
- 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
- 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
- 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
- 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
- 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
- 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
- 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
- 0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
- 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
- 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
- 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
- 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
- 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
- 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
- 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
- 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
- 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
- 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
- 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
- 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
- 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
- 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
- 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
- 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
- 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
- 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
- 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
- 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
- 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
- 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
- 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
- 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
- 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
- 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
- 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
- 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
- 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
- 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
- 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
- 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
- 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
- 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
- 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
- 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
- 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
- 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
- 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
- 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
- 0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54,
- 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
- 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
- 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
- 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
- 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
- 0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
- 0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b,
- 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
- 0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b,
- 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
- 0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
- 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
- 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
- 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
- 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
- 0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b,
- 0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
- 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
- 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
- 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
- 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
- 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
- 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
- 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
- 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
- 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
- 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
- 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
- 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
- 0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c,
- 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
- 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
- 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
- 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
- 0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80,
- 0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39,
- 0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80,
- 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
- 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
- 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
- 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
- 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
- 0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
- 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
- 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
- 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
- 0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
- 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
- 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
- 0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
- 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
- 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
- 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
- 0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
- 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
- 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
- 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
- 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
- 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
- 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
- 0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
- 0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
- 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
- 0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39,
- 0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
- 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0,
- 0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
- 0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b,
- 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
- 0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c,
- 0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e,
- 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
- 0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18,
- 0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10,
- 0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80,
- 0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10,
- 0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10,
- 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
- 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
- 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
- 0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
- 0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54,
- 0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44,
- 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54,
- 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
- 0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54,
- 0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44,
- 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54,
- 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
- 0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54,
- 0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44,
- 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54,
- 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
- 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54,
- 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44,
- 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54,
- 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00
-};
-
-static unsigned char alaw_main[] = {
- 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
- 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
- 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
- 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
- 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
- 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
- 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
- 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
- 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
- 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
- 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
- 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
- 0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
- 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
- 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
- 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
- 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
- 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
- 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
- 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
- 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
- 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
- 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
- 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
- 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
- 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
- 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
- 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
- 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
- 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
- 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
- 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
- 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
- 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
- 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
- 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
- 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
- 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
- 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
- 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
- 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
- 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
- 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
- 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
- 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
- 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
- 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
- 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
- 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
- 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
- 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
- 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
- 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
- 0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54,
- 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
- 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
- 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
- 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
- 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
- 0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
- 0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b,
- 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
- 0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b,
- 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
- 0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
- 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
- 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
- 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
- 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
- 0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b,
- 0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80,
- 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
- 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
- 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
- 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
- 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
- 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
- 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
- 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
- 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
- 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
- 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
- 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
- 0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c,
- 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
- 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
- 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
- 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
- 0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49,
- 0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39,
- 0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49,
- 0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39,
- 0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80,
- 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
- 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
- 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
- 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
- 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
- 0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
- 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
- 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
- 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
- 0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
- 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
- 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
- 0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
- 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
- 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
- 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
- 0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
- 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
- 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
- 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
- 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
- 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
- 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
- 0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
- 0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
- 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
- 0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10,
- 0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44,
- 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0,
- 0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44,
- 0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b,
- 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
- 0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c,
- 0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e,
- 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
- 0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10,
- 0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b,
- 0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80,
- 0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80,
- 0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19,
- 0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49,
- 0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18,
- 0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb,
- 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80,
- 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
- 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
- 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
- 0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
- 0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54,
- 0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44,
- 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54,
- 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
- 0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54,
- 0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44,
- 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54,
- 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
- 0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54,
- 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44,
- 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54,
- 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
- 0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54,
- 0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44,
- 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54,
- 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00
-};
-
-
-static unsigned char ima_adpcm_init[] = {
- 0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b,
- 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
- 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
- 0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e,
- 0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
- 0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
- 0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b,
- 0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b,
- 0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b,
- 0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b,
- 0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b,
- 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
- 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
- 0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
- 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19,
- 0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82,
- 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
- 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
- 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
- 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
- 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
- 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
- 0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19,
- 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
- 0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49,
- 0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00
-};
-
-static unsigned char ima_adpcm_playback[] = {
- 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
- 0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44,
- 0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
- 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49,
- 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
- 0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80,
- 0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b,
- 0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b,
- 0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
- 0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
- 0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b,
- 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
- 0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80,
- 0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
- 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
- 0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14,
- 0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8,
- 0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49,
- 0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39,
- 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
- 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
- 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
- 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
- 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
- 0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4,
- 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
- 0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
- 0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
- 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
- 0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
- 0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
- 0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
- 0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
- 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
- 0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
- 0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
- 0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
- 0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
- 0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
- 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
- 0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
- 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
- 0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
- 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
- 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
- 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
- 0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44,
- 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
- 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
- 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
- 0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
- 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
- 0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
- 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
- 0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
- 0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
- 0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84,
- 0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39,
- 0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19,
- 0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19,
- 0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
- 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
- 0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19,
- 0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
- 0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
- 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
- 0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54,
- 0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44,
- 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
- 0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44,
- 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
- 0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84,
- 0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
- 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
- 0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b,
- 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
- 0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49,
- 0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a,
- 0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a,
- 0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
- 0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44,
- 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
- 0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19,
- 0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b,
- 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
- 0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19,
- 0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39,
- 0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a,
- 0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19,
- 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
- 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
- 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
- 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
- 0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
- 0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11,
- 0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10,
- 0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18,
- 0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44,
- 0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4,
- 0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19,
- 0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
- 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
- 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
- 0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
- 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84,
- 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
- 0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
- 0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
- 0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44,
- 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
- 0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19,
- 0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44,
- 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
- 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
- 0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a,
- 0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39,
- 0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39,
- 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
- 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
- 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
- 0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e,
- 0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84,
- 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
- 0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44,
- 0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44,
- 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
- 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
- 0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
- 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84,
- 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
- 0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b,
- 0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80,
- 0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80,
- 0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e,
- 0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf,
- 0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b,
- 0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
- 0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b,
- 0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
- 0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b,
- 0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
- 0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80,
- 0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b,
- 0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80,
- 0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb,
- 0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
- 0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19,
- 0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a,
- 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e,
- 0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44,
- 0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44,
- 0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60,
- 0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f,
- 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
- 0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80,
- 0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2,
- 0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4,
- 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
- 0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4,
- 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
- 0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4,
- 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
- 0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11,
- 0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4,
- 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
- 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22,
- 0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80,
- 0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b,
- 0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a,
- 0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02,
- 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
- 0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60,
- 0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82,
- 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb,
- 0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b,
- 0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f,
- 0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f,
- 0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a,
- 0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
- 0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
- 0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02,
- 0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09,
- 0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a,
- 0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
- 0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
- 0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44,
- 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39,
- 0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a,
- 0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19,
- 0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a,
- 0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a,
- 0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54,
- 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44,
- 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54,
- 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
- 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54,
- 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44,
- 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54,
- 0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44,
- 0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf,
- 0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80,
- 0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22,
- 0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82,
- 0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82,
- 0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44,
- 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
- 0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
- 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
- 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
- 0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09,
- 0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80,
- 0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80,
- 0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a,
- 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
- 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a,
- 0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
- 0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
- 0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
- 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18,
- 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
- 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80,
- 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
- 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
- 0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80,
- 0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00
-};
-
-static unsigned char ima_adpcm_capture[] = {
- 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
- 0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44,
- 0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
- 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
- 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49,
- 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
- 0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b,
- 0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19,
- 0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b,
- 0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b,
- 0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19,
- 0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19,
- 0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19,
- 0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
- 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
- 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
- 0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14,
- 0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8,
- 0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24,
- 0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44,
- 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
- 0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18,
- 0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19,
- 0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8,
- 0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8,
- 0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
- 0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a,
- 0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a,
- 0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
- 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
- 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
- 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
- 0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4,
- 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
- 0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
- 0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
- 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
- 0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
- 0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
- 0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
- 0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
- 0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
- 0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
- 0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
- 0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
- 0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
- 0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
- 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
- 0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
- 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
- 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
- 0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
- 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
- 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
- 0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
- 0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44,
- 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
- 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
- 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
- 0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
- 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
- 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
- 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
- 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
- 0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
- 0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
- 0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84,
- 0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19,
- 0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
- 0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19,
- 0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
- 0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
- 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
- 0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19,
- 0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
- 0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
- 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
- 0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14,
- 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80,
- 0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49,
- 0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80,
- 0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b,
- 0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b,
- 0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80,
- 0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44,
- 0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
- 0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19,
- 0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44,
- 0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
- 0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4,
- 0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44,
- 0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e,
- 0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84,
- 0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10,
- 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
- 0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b,
- 0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10,
- 0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80,
- 0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a,
- 0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4,
- 0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4,
- 0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4,
- 0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44,
- 0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18,
- 0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
- 0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19,
- 0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19,
- 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
- 0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02,
- 0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
- 0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f,
- 0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44,
- 0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
- 0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44,
- 0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f,
- 0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b,
- 0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b,
- 0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80,
- 0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84,
- 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
- 0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b,
- 0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a,
- 0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
- 0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a,
- 0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44,
- 0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18,
- 0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb,
- 0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a,
- 0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8,
- 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
- 0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
- 0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44,
- 0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80,
- 0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4,
- 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
- 0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
- 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
- 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
- 0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02,
- 0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09,
- 0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44,
- 0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f,
- 0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88,
- 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82,
- 0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49,
- 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
- 0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
- 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
- 0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f,
- 0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e,
- 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
- 0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80,
- 0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14,
- 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
- 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
- 0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54,
- 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
- 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
- 0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54,
- 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
- 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
- 0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54,
- 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
- 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
- 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
- 0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4,
- 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
- 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
- 0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
- 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e,
- 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
- 0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
- 0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
- 0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
- 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
- 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
- 0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60,
- 0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46,
- 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
- 0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0,
- 0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14,
- 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
- 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
- 0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54,
- 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
- 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
- 0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54,
- 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
- 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
- 0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54,
- 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
- 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
- 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
- 0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4,
- 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
- 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
- 0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
- 0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e,
- 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
- 0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
- 0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
- 0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
- 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
- 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
- 0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46,
- 0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
- 0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80,
- 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
- 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46,
- 0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14,
- 0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a,
- 0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19,
- 0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a,
- 0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19,
- 0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a,
- 0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19,
- 0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a,
- 0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19,
- 0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a,
- 0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19,
- 0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a,
- 0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19,
- 0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a,
- 0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19,
- 0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a,
- 0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19,
- 0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b,
- 0xa6, 0xc5, 0x11, 0x00
-};
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
index f7e8192270a..2a6cc1cfe94 100644
--- a/sound/isa/sb/sb16_main.c
+++ b/sound/isa/sb/sb16_main.c
@@ -669,7 +669,8 @@ static int snd_sb16_capture_close(struct snd_pcm_substream *substream)
static int snd_sb16_set_dma_mode(struct snd_sb *chip, int what)
{
if (chip->dma8 < 0 || chip->dma16 < 0) {
- snd_assert(what == 0, return -EINVAL);
+ if (snd_BUG_ON(what))
+ return -EINVAL;
return 0;
}
if (what == 0) {
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
index fe03bb82053..658d55769c9 100644
--- a/sound/isa/sb/sb8_main.c
+++ b/sound/isa/sb/sb8_main.c
@@ -111,7 +111,9 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
switch (chip->hardware) {
case SB_HW_PRO:
if (runtime->channels > 1) {
- snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+ if (snd_BUG_ON(rate != SB8_RATE(11025) &&
+ rate != SB8_RATE(22050)))
+ return -EINVAL;
chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
break;
}
@@ -237,7 +239,9 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
switch (chip->hardware) {
case SB_HW_PRO:
if (runtime->channels > 1) {
- snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+ if (snd_BUG_ON(rate != SB8_RATE(11025) &&
+ rate != SB8_RATE(22050)))
+ return -EINVAL;
chip->capture_format = SB_DSP_HI_INPUT_AUTO;
break;
}
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index b432d9ae874..27a65150225 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -219,7 +219,8 @@ int snd_sbdsp_create(struct snd_card *card,
.dev_free = snd_sbdsp_dev_free,
};
- snd_assert(r_chip != NULL, return -EINVAL);
+ if (snd_BUG_ON(!r_chip))
+ return -EINVAL;
*r_chip = NULL;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 73d4572d136..406a431af91 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -792,7 +792,8 @@ int snd_sbmixer_new(struct snd_sb *chip)
struct snd_card *card;
int err;
- snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip || !chip->card))
+ return -EINVAL;
card = chip->card;
@@ -925,7 +926,8 @@ static unsigned char als4000_saved_regs[] = {
static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
{
unsigned char *val = chip->saved_regs;
- snd_assert(num_regs <= ARRAY_SIZE(chip->saved_regs), return);
+ if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
+ return;
for (; num_regs; num_regs--)
*val++ = snd_sbmixer_read(chip, *regs++);
}
@@ -933,7 +935,8 @@ static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
{
unsigned char *val = chip->saved_regs;
- snd_assert(num_regs <= ARRAY_SIZE(chip->saved_regs), return);
+ if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
+ return;
for (; num_regs; num_regs--)
snd_sbmixer_write(chip, *regs++, *val++);
}
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index da3d152bcad..ca35924dc3b 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -29,7 +29,7 @@
#include <linux/io.h>
#include <asm/dma.h>
#include <sound/core.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
#include <sound/opl3.h>
#include <sound/mpu401.h>
#include <sound/control.h>
@@ -397,7 +397,7 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
return 0;
}
-static int __devinit snd_sc6000_mixer(struct snd_ad1848 *chip)
+static int __devinit snd_sc6000_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -483,7 +483,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
int xirq = irq[dev];
int xdma = dma[dev];
struct snd_card *card;
- struct snd_ad1848 *chip;
+ struct snd_wss *chip;
struct snd_opl3 *opl3;
char __iomem *vport;
char __iomem *vmss_port;
@@ -548,21 +548,21 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
if (err < 0)
goto err_unmap2;
- err = snd_ad1848_create(card, mss_port[dev] + 4, xirq, xdma,
- AD1848_HW_DETECT, &chip);
+ err = snd_wss_create(card, mss_port[dev] + 4, -1, xirq, xdma, -1,
+ WSS_HW_DETECT, 0, &chip);
if (err < 0)
goto err_unmap2;
card->private_data = chip;
- err = snd_ad1848_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0, NULL);
if (err < 0) {
snd_printk(KERN_ERR PFX
- "error creating new ad1848 PCM device\n");
+ "error creating new WSS PCM device\n");
goto err_unmap2;
}
- err = snd_ad1848_mixer(chip);
+ err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR PFX "error creating new ad1848 mixer\n");
+ snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
goto err_unmap2;
}
err = snd_sc6000_mixer(chip);
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
index a07274ecb14..2c7503bf127 100644
--- a/sound/isa/sgalaxy.c
+++ b/sound/isa/sgalaxy.c
@@ -31,7 +31,7 @@
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/sb.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
#include <sound/control.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
@@ -175,12 +175,14 @@ static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)
return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
}
-static struct ad1848_mix_elem snd_sgalaxy_controls[] = {
-AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
-AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
+static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
+WSS_DOUBLE("Aux Playback Switch", 0,
+ SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 0,
+ SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
};
-static int __devinit snd_sgalaxy_mixer(struct snd_ad1848 *chip)
+static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -210,7 +212,9 @@ static int __devinit snd_sgalaxy_mixer(struct snd_ad1848 *chip)
return err;
/* build AUX2 input */
for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
- if ((err = snd_ad1848_add_ctl_elem(chip, &snd_sgalaxy_controls[idx])) < 0)
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
+ if (err < 0)
return err;
}
return 0;
@@ -237,7 +241,7 @@ static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
static int possible_dmas[] = {1, 3, 0, -1};
int err, xirq, xdma1;
struct snd_card *card;
- struct snd_ad1848 *chip;
+ struct snd_wss *chip;
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
@@ -263,18 +267,21 @@ static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
goto _err;
- if ((err = snd_ad1848_create(card, wssport[dev] + 4,
- xirq, xdma1,
- AD1848_HW_DETECT, &chip)) < 0)
+ err = snd_wss_create(card, wssport[dev] + 4, -1,
+ xirq, xdma1, -1,
+ WSS_HW_DETECT, 0, &chip);
+ if (err < 0)
goto _err;
card->private_data = chip;
- if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) {
- snd_printdd(PFX "error creating new ad1848 PCM device\n");
+ err = snd_wss_pcm(chip, 0, NULL);
+ if (err < 0) {
+ snd_printdd(PFX "error creating new WSS PCM device\n");
goto _err;
}
- if ((err = snd_ad1848_mixer(chip)) < 0) {
- snd_printdd(PFX "error creating new ad1848 mixer\n");
+ err = snd_wss_mixer(chip);
+ if (err < 0) {
+ snd_printdd(PFX "error creating new WSS mixer\n");
goto _err;
}
if ((err = snd_sgalaxy_mixer(chip)) < 0) {
@@ -312,7 +319,7 @@ static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
pm_message_t state)
{
struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_ad1848 *chip = card->private_data;
+ struct snd_wss *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->suspend(chip);
@@ -322,11 +329,11 @@ static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
{
struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_ad1848 *chip = card->private_data;
+ struct snd_wss *chip = card->private_data;
chip->resume(chip);
- snd_ad1848_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
- snd_ad1848_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
+ snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
+ snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 06ad7863dff..48a16d86583 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -31,7 +31,7 @@
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/hwdep.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
@@ -147,7 +147,7 @@ struct soundscape {
enum card_type type;
struct resource *io_res;
struct resource *wss_res;
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
struct snd_mpu401 *mpu;
struct snd_hwdep *hw;
@@ -726,7 +726,7 @@ static int sscape_midi_info(struct snd_kcontrol *ctl,
static int sscape_midi_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uctl)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kctl);
+ struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
register struct soundscape *s = get_card_soundscape(card);
unsigned long flags;
@@ -746,7 +746,7 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,
static int sscape_midi_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uctl)
{
- struct snd_cs4231 *chip = snd_kcontrol_chip(kctl);
+ struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
register struct soundscape *s = get_card_soundscape(card);
unsigned long flags;
@@ -958,7 +958,9 @@ static int __devinit create_mpu401(struct snd_card *card, int devnum, unsigned l
* Override for the CS4231 playback format function.
* The AD1845 has much simpler format and rate selection.
*/
-static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_params *params, unsigned char format)
+static void ad1845_playback_format(struct snd_wss *chip,
+ struct snd_pcm_hw_params *params,
+ unsigned char format)
{
unsigned long flags;
unsigned rate = params_rate(params);
@@ -983,9 +985,9 @@ static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_p
* NOTE: We seem to need to write to the MSB before the LSB
* to get the correct sample frequency.
*/
- snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0));
- snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
- snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0));
+ snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
+ snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
@@ -994,7 +996,9 @@ static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_p
* Override for the CS4231 capture format function.
* The AD1845 has much simpler format and rate selection.
*/
-static void ad1845_capture_format(struct snd_cs4231 * chip, struct snd_pcm_hw_params *params, unsigned char format)
+static void ad1845_capture_format(struct snd_wss *chip,
+ struct snd_pcm_hw_params *params,
+ unsigned char format)
{
unsigned long flags;
unsigned rate = params_rate(params);
@@ -1019,9 +1023,9 @@ static void ad1845_capture_format(struct snd_cs4231 * chip, struct snd_pcm_hw_pa
* NOTE: We seem to need to write to the MSB before the LSB
* to get the correct sample frequency.
*/
- snd_cs4231_out(chip, CS4231_REC_FORMAT, (format & 0xf0));
- snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
- snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
+ snd_wss_out(chip, CS4231_REC_FORMAT, (format & 0xf0));
+ snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
+ snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
@@ -1036,7 +1040,7 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
int irq, int dma1, int dma2)
{
register struct soundscape *sscape = get_card_soundscape(card);
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
int err;
if (sscape->type == SSCAPE_VIVO)
@@ -1045,9 +1049,8 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
if (dma1 == dma2)
dma2 = -1;
- err = snd_cs4231_create(card,
- port, -1, irq, dma1, dma2,
- CS4231_HW_DETECT, CS4231_HWSHARE_DMA1, &chip);
+ err = snd_wss_create(card, port, -1, irq, dma1, dma2,
+ WSS_HW_DETECT, WSS_HWSHARE_DMA1, &chip);
if (!err) {
unsigned long flags;
struct snd_pcm *pcm;
@@ -1063,11 +1066,11 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
*
#define AD1845_IFACE_CONFIG \
(CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE)
- snd_cs4231_mce_up(chip);
+ snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
+ snd_wss_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
+ snd_wss_mce_down(chip);
*/
if (sscape->type != SSCAPE_VIVO) {
@@ -1077,11 +1080,11 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
* be 14.31818 MHz, because we must set this register
* to get the playback to sound correct ...
*/
- snd_cs4231_mce_up(chip);
+ snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);
+ snd_wss_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_cs4231_mce_down(chip);
+ snd_wss_mce_down(chip);
/*
* More custom configuration:
@@ -1089,28 +1092,28 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
* b) enable frequency selection (for capture/playback)
*/
spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs4231_out(chip, CS4231_MISC_INFO,
- CS4231_MODE2 | 0x10);
- val = snd_cs4231_in(chip, AD1845_PWR_DOWN_CTRL);
- snd_cs4231_out(chip, AD1845_PWR_DOWN_CTRL,
- val | AD1845_FREQ_SEL_ENABLE);
+ snd_wss_out(chip, CS4231_MISC_INFO,
+ CS4231_MODE2 | 0x10);
+ val = snd_wss_in(chip, AD1845_PWR_DOWN_CTRL);
+ snd_wss_out(chip, AD1845_PWR_DOWN_CTRL,
+ val | AD1845_FREQ_SEL_ENABLE);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
- err = snd_cs4231_pcm(chip, 0, &pcm);
+ err = snd_wss_pcm(chip, 0, &pcm);
if (err < 0) {
snd_printk(KERN_ERR "sscape: No PCM device "
"for AD1845 chip\n");
goto _error;
}
- err = snd_cs4231_mixer(chip);
+ err = snd_wss_mixer(chip);
if (err < 0) {
snd_printk(KERN_ERR "sscape: No mixer device "
"for AD1845 chip\n");
goto _error;
}
- err = snd_cs4231_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0, NULL);
if (err < 0) {
snd_printk(KERN_ERR "sscape: No timer device "
"for AD1845 chip\n");
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 3a6c6fe1ec4..4c095bc7c72 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -1,6 +1,6 @@
/*
* ALSA card-level driver for Turtle Beach Wavefront cards
- * (Maui,Tropez,Tropez+)
+ * (Maui,Tropez,Tropez+)
*
* Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>
*
@@ -29,6 +29,7 @@
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/opl3.h>
+#include <sound/wss.h>
#include <sound/snd_wavefront.h>
MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
@@ -319,8 +320,8 @@ snd_wavefront_new_midi (struct snd_card *card,
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
- SNDRV_RAWMIDI_INFO_INPUT |
- SNDRV_RAWMIDI_INFO_DUPLEX;
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
return rmidi;
}
@@ -363,7 +364,7 @@ static int __devinit
snd_wavefront_probe (struct snd_card *card, int dev)
{
snd_wavefront_card_t *acard = card->private_data;
- struct snd_cs4231 *chip;
+ struct snd_wss *chip;
struct snd_hwdep *wavefront_synth;
struct snd_rawmidi *ics2115_internal_rmidi = NULL;
struct snd_rawmidi *ics2115_external_rmidi = NULL;
@@ -372,21 +373,20 @@ snd_wavefront_probe (struct snd_card *card, int dev)
/* --------- PCM --------------- */
- if ((err = snd_cs4231_create (card,
- cs4232_pcm_port[dev],
- -1,
- cs4232_pcm_irq[dev],
- dma1[dev],
- dma2[dev],
- CS4231_HW_DETECT, 0, &chip)) < 0) {
- snd_printk (KERN_ERR "can't allocate CS4231 device\n");
+ err = snd_wss_create(card, cs4232_pcm_port[dev], -1,
+ cs4232_pcm_irq[dev], dma1[dev], dma2[dev],
+ WSS_HW_DETECT, 0, &chip);
+ if (err < 0) {
+ snd_printk(KERN_ERR "can't allocate WSS device\n");
return err;
}
- if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0)
+ err = snd_wss_pcm(chip, 0, NULL);
+ if (err < 0)
return err;
- if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0)
+ err = snd_wss_timer(chip, 0, NULL);
+ if (err < 0)
return err;
/* ---------- OPL3 synth --------- */
@@ -394,24 +394,24 @@ snd_wavefront_probe (struct snd_card *card, int dev)
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
struct snd_opl3 *opl3;
- if ((err = snd_opl3_create(card,
- fm_port[dev],
- fm_port[dev] + 2,
- OPL3_HW_OPL3_CS,
- 0, &opl3)) < 0) {
+ err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+ OPL3_HW_OPL3_CS, 0, &opl3);
+ if (err < 0) {
snd_printk (KERN_ERR "can't allocate or detect OPL3 synth\n");
return err;
}
- if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0)
+ err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL);
+ if (err < 0)
return err;
hw_dev++;
}
/* ------- ICS2115 Wavetable synth ------- */
- if ((acard->wavefront.res_base = request_region(ics2115_port[dev], 16,
- "ICS2115")) == NULL) {
+ acard->wavefront.res_base = request_region(ics2115_port[dev], 16,
+ "ICS2115");
+ if (acard->wavefront.res_base == NULL) {
snd_printk(KERN_ERR "unable to grab ICS2115 i/o region 0x%lx-0x%lx\n",
ics2115_port[dev], ics2115_port[dev] + 16 - 1);
return -EBUSY;
@@ -425,7 +425,8 @@ snd_wavefront_probe (struct snd_card *card, int dev)
acard->wavefront.irq = ics2115_irq[dev];
acard->wavefront.base = ics2115_port[dev];
- if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) {
+ wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard);
+ if (wavefront_synth == NULL) {
snd_printk (KERN_ERR "can't create WaveFront synth device\n");
return -ENOMEM;
}
@@ -436,7 +437,8 @@ snd_wavefront_probe (struct snd_card *card, int dev)
/* --------- Mixer ------------ */
- if ((err = snd_cs4231_mixer(chip)) < 0) {
+ err = snd_wss_mixer(chip);
+ if (err < 0) {
snd_printk (KERN_ERR "can't allocate mixer device\n");
return err;
}
@@ -444,11 +446,11 @@ snd_wavefront_probe (struct snd_card *card, int dev)
/* -------- CS4232 MPU-401 interface -------- */
if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
- if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
- cs4232_mpu_port[dev], 0,
- cs4232_mpu_irq[dev],
- IRQF_DISABLED,
- NULL)) < 0) {
+ err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
+ cs4232_mpu_port[dev], 0,
+ cs4232_mpu_irq[dev], IRQF_DISABLED,
+ NULL);
+ if (err < 0) {
snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
return err;
}
@@ -601,7 +603,7 @@ static struct isa_driver snd_wavefront_driver = {
#ifdef CONFIG_PNP
static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 2efaa7f205a..dfc449a2194 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -180,11 +180,11 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
unsigned short *pd;
int err = 0;
- snd_assert(sdev->card != NULL, return -ENODEV);
-
card = sdev->card;
-
- snd_assert(card->private_data != NULL, return -ENODEV);
+ if (snd_BUG_ON(!card))
+ return -ENODEV;
+ if (snd_BUG_ON(!card->private_data))
+ return -ENODEV;
acard = card->private_data;
dev = &acard->wavefront;
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index a33384a55b0..f14a7c0b699 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -235,8 +235,10 @@ static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
- snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
- snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+ if (snd_BUG_ON(!substream || !substream->rmidi))
+ return -ENXIO;
+ if (snd_BUG_ON(!substream->rmidi->private_data))
+ return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
@@ -257,8 +259,10 @@ static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substrea
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
- snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
- snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+ if (snd_BUG_ON(!substream || !substream->rmidi))
+ return -ENXIO;
+ if (snd_BUG_ON(!substream->rmidi->private_data))
+ return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
@@ -279,8 +283,10 @@ static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substrea
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
- snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
- snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+ if (snd_BUG_ON(!substream || !substream->rmidi))
+ return -ENXIO;
+ if (snd_BUG_ON(!substream->rmidi->private_data))
+ return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
@@ -300,8 +306,10 @@ static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substre
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
- snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
- snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+ if (snd_BUG_ON(!substream || !substream->rmidi))
+ return -ENXIO;
+ if (snd_BUG_ON(!substream->rmidi->private_data))
+ return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 95eeca16335..4c410820a99 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1648,9 +1648,10 @@ snd_wavefront_synth_ioctl (struct snd_hwdep *hw, struct file *file,
card = (struct snd_card *) hw->card;
- snd_assert(card != NULL, return -ENODEV);
-
- snd_assert(card->private_data != NULL, return -ENODEV);
+ if (snd_BUG_ON(!card))
+ return -ENODEV;
+ if (snd_BUG_ON(!card->private_data))
+ return -ENODEV;
acard = card->private_data;
dev = &acard->wavefront;
@@ -1939,7 +1940,7 @@ static int __devinit
wavefront_download_firmware (snd_wavefront_t *dev, char *path)
{
- unsigned char *buf;
+ const unsigned char *buf;
int len, err;
int section_cnt_downloaded = 0;
const struct firmware *firmware;
diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile
new file mode 100644
index 00000000000..454fee769a3
--- /dev/null
+++ b/sound/isa/wss/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz>
+#
+
+snd-wss-lib-objs := wss_lib.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_WSS_LIB) += snd-wss-lib.o
+
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
new file mode 100644
index 00000000000..3d6c5f2838a
--- /dev/null
+++ b/sound/isa/wss/wss_lib.c
@@ -0,0 +1,2322 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
+ *
+ * Bugs:
+ * - sometimes record brokes playback with WSS portion of
+ * Yamaha OPL3-SA3 chip
+ * - CS4231 (GUS MAX) - still trouble with occasional noises
+ * - broken initialization?
+ *
+ * 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/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/wss.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
+MODULE_LICENSE("GPL");
+
+#if 0
+#define SNDRV_DEBUG_MCE
+#endif
+
+/*
+ * Some variables
+ */
+
+static unsigned char freq_bits[14] = {
+ /* 5510 */ 0x00 | CS4231_XTAL2,
+ /* 6620 */ 0x0E | CS4231_XTAL2,
+ /* 8000 */ 0x00 | CS4231_XTAL1,
+ /* 9600 */ 0x0E | CS4231_XTAL1,
+ /* 11025 */ 0x02 | CS4231_XTAL2,
+ /* 16000 */ 0x02 | CS4231_XTAL1,
+ /* 18900 */ 0x04 | CS4231_XTAL2,
+ /* 22050 */ 0x06 | CS4231_XTAL2,
+ /* 27042 */ 0x04 | CS4231_XTAL1,
+ /* 32000 */ 0x06 | CS4231_XTAL1,
+ /* 33075 */ 0x0C | CS4231_XTAL2,
+ /* 37800 */ 0x08 | CS4231_XTAL2,
+ /* 44100 */ 0x0A | CS4231_XTAL2,
+ /* 48000 */ 0x0C | CS4231_XTAL1
+};
+
+static unsigned int rates[14] = {
+ 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
+ 27042, 32000, 33075, 37800, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int snd_wss_xrate(struct snd_pcm_runtime *runtime)
+{
+ return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_rates);
+}
+
+static unsigned char snd_wss_original_image[32] =
+{
+ 0x00, /* 00/00 - lic */
+ 0x00, /* 01/01 - ric */
+ 0x9f, /* 02/02 - la1ic */
+ 0x9f, /* 03/03 - ra1ic */
+ 0x9f, /* 04/04 - la2ic */
+ 0x9f, /* 05/05 - ra2ic */
+ 0xbf, /* 06/06 - loc */
+ 0xbf, /* 07/07 - roc */
+ 0x20, /* 08/08 - pdfr */
+ CS4231_AUTOCALIB, /* 09/09 - ic */
+ 0x00, /* 0a/10 - pc */
+ 0x00, /* 0b/11 - ti */
+ CS4231_MODE2, /* 0c/12 - mi */
+ 0xfc, /* 0d/13 - lbc */
+ 0x00, /* 0e/14 - pbru */
+ 0x00, /* 0f/15 - pbrl */
+ 0x80, /* 10/16 - afei */
+ 0x01, /* 11/17 - afeii */
+ 0x9f, /* 12/18 - llic */
+ 0x9f, /* 13/19 - rlic */
+ 0x00, /* 14/20 - tlb */
+ 0x00, /* 15/21 - thb */
+ 0x00, /* 16/22 - la3mic/reserved */
+ 0x00, /* 17/23 - ra3mic/reserved */
+ 0x00, /* 18/24 - afs */
+ 0x00, /* 19/25 - lamoc/version */
+ 0xcf, /* 1a/26 - mioc */
+ 0x00, /* 1b/27 - ramoc/reserved */
+ 0x20, /* 1c/28 - cdfr */
+ 0x00, /* 1d/29 - res4 */
+ 0x00, /* 1e/30 - cbru */
+ 0x00, /* 1f/31 - cbrl */
+};
+
+static unsigned char snd_opti93x_original_image[32] =
+{
+ 0x00, /* 00/00 - l_mixout_outctrl */
+ 0x00, /* 01/01 - r_mixout_outctrl */
+ 0x88, /* 02/02 - l_cd_inctrl */
+ 0x88, /* 03/03 - r_cd_inctrl */
+ 0x88, /* 04/04 - l_a1/fm_inctrl */
+ 0x88, /* 05/05 - r_a1/fm_inctrl */
+ 0x80, /* 06/06 - l_dac_inctrl */
+ 0x80, /* 07/07 - r_dac_inctrl */
+ 0x00, /* 08/08 - ply_dataform_reg */
+ 0x00, /* 09/09 - if_conf */
+ 0x00, /* 0a/10 - pin_ctrl */
+ 0x00, /* 0b/11 - err_init_reg */
+ 0x0a, /* 0c/12 - id_reg */
+ 0x00, /* 0d/13 - reserved */
+ 0x00, /* 0e/14 - ply_upcount_reg */
+ 0x00, /* 0f/15 - ply_lowcount_reg */
+ 0x88, /* 10/16 - reserved/l_a1_inctrl */
+ 0x88, /* 11/17 - reserved/r_a1_inctrl */
+ 0x88, /* 12/18 - l_line_inctrl */
+ 0x88, /* 13/19 - r_line_inctrl */
+ 0x88, /* 14/20 - l_mic_inctrl */
+ 0x88, /* 15/21 - r_mic_inctrl */
+ 0x80, /* 16/22 - l_out_outctrl */
+ 0x80, /* 17/23 - r_out_outctrl */
+ 0x00, /* 18/24 - reserved */
+ 0x00, /* 19/25 - reserved */
+ 0x00, /* 1a/26 - reserved */
+ 0x00, /* 1b/27 - reserved */
+ 0x00, /* 1c/28 - cap_dataform_reg */
+ 0x00, /* 1d/29 - reserved */
+ 0x00, /* 1e/30 - cap_upcount_reg */
+ 0x00 /* 1f/31 - cap_lowcount_reg */
+};
+
+/*
+ * Basic I/O functions
+ */
+
+static inline void wss_outb(struct snd_wss *chip, u8 offset, u8 val)
+{
+ outb(val, chip->port + offset);
+}
+
+static inline u8 wss_inb(struct snd_wss *chip, u8 offset)
+{
+ return inb(chip->port + offset);
+}
+
+static void snd_wss_wait(struct snd_wss *chip)
+{
+ int timeout;
+
+ for (timeout = 250;
+ timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
+ timeout--)
+ udelay(100);
+}
+
+static void snd_wss_outm(struct snd_wss *chip, unsigned char reg,
+ unsigned char mask, unsigned char value)
+{
+ unsigned char tmp = (chip->image[reg] & mask) | value;
+
+ snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+ chip->image[reg] = tmp;
+ if (!chip->calibrate_mute) {
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+ wmb();
+ wss_outb(chip, CS4231P(REG), tmp);
+ mb();
+ }
+}
+
+static void snd_wss_dout(struct snd_wss *chip, unsigned char reg,
+ unsigned char value)
+{
+ int timeout;
+
+ for (timeout = 250;
+ timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
+ timeout--)
+ udelay(10);
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+ wss_outb(chip, CS4231P(REG), value);
+ mb();
+}
+
+void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char value)
+{
+ snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+ wss_outb(chip, CS4231P(REG), value);
+ chip->image[reg] = value;
+ mb();
+ snd_printdd("codec out - reg 0x%x = 0x%x\n",
+ chip->mce_bit | reg, value);
+}
+EXPORT_SYMBOL(snd_wss_out);
+
+unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg)
+{
+ snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
+#endif
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+ mb();
+ return wss_inb(chip, CS4231P(REG));
+}
+EXPORT_SYMBOL(snd_wss_in);
+
+void snd_cs4236_ext_out(struct snd_wss *chip, unsigned char reg,
+ unsigned char val)
+{
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
+ wss_outb(chip, CS4231P(REG),
+ reg | (chip->image[CS4236_EXT_REG] & 0x01));
+ wss_outb(chip, CS4231P(REG), val);
+ chip->eimage[CS4236_REG(reg)] = val;
+#if 0
+ printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+#endif
+}
+EXPORT_SYMBOL(snd_cs4236_ext_out);
+
+unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg)
+{
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
+ wss_outb(chip, CS4231P(REG),
+ reg | (chip->image[CS4236_EXT_REG] & 0x01));
+#if 1
+ return wss_inb(chip, CS4231P(REG));
+#else
+ {
+ unsigned char res;
+ res = wss_inb(chip, CS4231P(REG));
+ printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
+ return res;
+ }
+#endif
+}
+EXPORT_SYMBOL(snd_cs4236_ext_in);
+
+#if 0
+
+static void snd_wss_debug(struct snd_wss *chip)
+{
+ printk(KERN_DEBUG
+ "CS4231 REGS: INDEX = 0x%02x "
+ " STATUS = 0x%02x\n",
+ wss_inb(chip, CS4231P(REGSEL)),
+ wss_inb(chip, CS4231P(STATUS)));
+ printk(KERN_DEBUG
+ " 0x00: left input = 0x%02x "
+ " 0x10: alt 1 (CFIG 2) = 0x%02x\n",
+ snd_wss_in(chip, 0x00),
+ snd_wss_in(chip, 0x10));
+ printk(KERN_DEBUG
+ " 0x01: right input = 0x%02x "
+ " 0x11: alt 2 (CFIG 3) = 0x%02x\n",
+ snd_wss_in(chip, 0x01),
+ snd_wss_in(chip, 0x11));
+ printk(KERN_DEBUG
+ " 0x02: GF1 left input = 0x%02x "
+ " 0x12: left line in = 0x%02x\n",
+ snd_wss_in(chip, 0x02),
+ snd_wss_in(chip, 0x12));
+ printk(KERN_DEBUG
+ " 0x03: GF1 right input = 0x%02x "
+ " 0x13: right line in = 0x%02x\n",
+ snd_wss_in(chip, 0x03),
+ snd_wss_in(chip, 0x13));
+ printk(KERN_DEBUG
+ " 0x04: CD left input = 0x%02x "
+ " 0x14: timer low = 0x%02x\n",
+ snd_wss_in(chip, 0x04),
+ snd_wss_in(chip, 0x14));
+ printk(KERN_DEBUG
+ " 0x05: CD right input = 0x%02x "
+ " 0x15: timer high = 0x%02x\n",
+ snd_wss_in(chip, 0x05),
+ snd_wss_in(chip, 0x15));
+ printk(KERN_DEBUG
+ " 0x06: left output = 0x%02x "
+ " 0x16: left MIC (PnP) = 0x%02x\n",
+ snd_wss_in(chip, 0x06),
+ snd_wss_in(chip, 0x16));
+ printk(KERN_DEBUG
+ " 0x07: right output = 0x%02x "
+ " 0x17: right MIC (PnP) = 0x%02x\n",
+ snd_wss_in(chip, 0x07),
+ snd_wss_in(chip, 0x17));
+ printk(KERN_DEBUG
+ " 0x08: playback format = 0x%02x "
+ " 0x18: IRQ status = 0x%02x\n",
+ snd_wss_in(chip, 0x08),
+ snd_wss_in(chip, 0x18));
+ printk(KERN_DEBUG
+ " 0x09: iface (CFIG 1) = 0x%02x "
+ " 0x19: left line out = 0x%02x\n",
+ snd_wss_in(chip, 0x09),
+ snd_wss_in(chip, 0x19));
+ printk(KERN_DEBUG
+ " 0x0a: pin control = 0x%02x "
+ " 0x1a: mono control = 0x%02x\n",
+ snd_wss_in(chip, 0x0a),
+ snd_wss_in(chip, 0x1a));
+ printk(KERN_DEBUG
+ " 0x0b: init & status = 0x%02x "
+ " 0x1b: right line out = 0x%02x\n",
+ snd_wss_in(chip, 0x0b),
+ snd_wss_in(chip, 0x1b));
+ printk(KERN_DEBUG
+ " 0x0c: revision & mode = 0x%02x "
+ " 0x1c: record format = 0x%02x\n",
+ snd_wss_in(chip, 0x0c),
+ snd_wss_in(chip, 0x1c));
+ printk(KERN_DEBUG
+ " 0x0d: loopback = 0x%02x "
+ " 0x1d: var freq (PnP) = 0x%02x\n",
+ snd_wss_in(chip, 0x0d),
+ snd_wss_in(chip, 0x1d));
+ printk(KERN_DEBUG
+ " 0x0e: ply upr count = 0x%02x "
+ " 0x1e: ply lwr count = 0x%02x\n",
+ snd_wss_in(chip, 0x0e),
+ snd_wss_in(chip, 0x1e));
+ printk(KERN_DEBUG
+ " 0x0f: rec upr count = 0x%02x "
+ " 0x1f: rec lwr count = 0x%02x\n",
+ snd_wss_in(chip, 0x0f),
+ snd_wss_in(chip, 0x1f));
+}
+
+#endif
+
+/*
+ * CS4231 detection / MCE routines
+ */
+
+static void snd_wss_busy_wait(struct snd_wss *chip)
+{
+ int timeout;
+
+ /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
+ for (timeout = 5; timeout > 0; timeout--)
+ wss_inb(chip, CS4231P(REGSEL));
+ /* end of cleanup sequence */
+ for (timeout = 25000;
+ timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
+ timeout--)
+ udelay(10);
+}
+
+void snd_wss_mce_up(struct snd_wss *chip)
+{
+ unsigned long flags;
+ int timeout;
+
+ snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ snd_printk("mce_up - auto calibration time out (0)\n");
+#endif
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->mce_bit |= CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ if (timeout == 0x80)
+ snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+ if (!(timeout & CS4231_MCE))
+ wss_outb(chip, CS4231P(REGSEL),
+ chip->mce_bit | (timeout & 0x1f));
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+EXPORT_SYMBOL(snd_wss_mce_up);
+
+void snd_wss_mce_down(struct snd_wss *chip)
+{
+ unsigned long flags;
+ unsigned long end_time;
+ int timeout;
+ int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848;
+
+ snd_wss_busy_wait(chip);
+
+#ifdef CONFIG_SND_DEBUG
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL));
+#endif
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->mce_bit &= ~CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (timeout == 0x80)
+ snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+ if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask))
+ return;
+
+ /*
+ * Wait for (possible -- during init auto-calibration may not be set)
+ * calibration process to start. Needs upto 5 sample periods on AD1848
+ * which at the slowest possible rate of 5.5125 kHz means 907 us.
+ */
+ msleep(1);
+
+ snd_printdd("(1) jiffies = %lu\n", jiffies);
+
+ /* check condition up to 250 ms */
+ end_time = jiffies + msecs_to_jiffies(250);
+ while (snd_wss_in(chip, CS4231_TEST_INIT) &
+ CS4231_CALIB_IN_PROGRESS) {
+
+ if (time_after(jiffies, end_time)) {
+ snd_printk(KERN_ERR "mce_down - "
+ "auto calibration time out (2)\n");
+ return;
+ }
+ msleep(1);
+ }
+
+ snd_printdd("(2) jiffies = %lu\n", jiffies);
+
+ /* check condition up to 100 ms */
+ end_time = jiffies + msecs_to_jiffies(100);
+ while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
+ if (time_after(jiffies, end_time)) {
+ snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
+ return;
+ }
+ msleep(1);
+ }
+
+ snd_printdd("(3) jiffies = %lu\n", jiffies);
+ snd_printd("mce_down - exit = 0x%x\n", wss_inb(chip, CS4231P(REGSEL)));
+}
+EXPORT_SYMBOL(snd_wss_mce_down);
+
+static unsigned int snd_wss_get_count(unsigned char format, unsigned int size)
+{
+ switch (format & 0xe0) {
+ case CS4231_LINEAR_16:
+ case CS4231_LINEAR_16_BIG:
+ size >>= 1;
+ break;
+ case CS4231_ADPCM_16:
+ return size >> 2;
+ }
+ if (format & CS4231_STEREO)
+ size >>= 1;
+ return size;
+}
+
+static int snd_wss_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ int result = 0;
+ unsigned int what;
+ struct snd_pcm_substream *s;
+ int do_start;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ do_start = 1; break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ do_start = 0; break;
+ default:
+ return -EINVAL;
+ }
+
+ what = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s == chip->playback_substream) {
+ what |= CS4231_PLAYBACK_ENABLE;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == chip->capture_substream) {
+ what |= CS4231_RECORD_ENABLE;
+ snd_pcm_trigger_done(s, substream);
+ }
+ }
+ spin_lock(&chip->reg_lock);
+ if (do_start) {
+ chip->image[CS4231_IFACE_CTRL] |= what;
+ if (chip->trigger)
+ chip->trigger(chip, what, 1);
+ } else {
+ chip->image[CS4231_IFACE_CTRL] &= ~what;
+ if (chip->trigger)
+ chip->trigger(chip, what, 0);
+ }
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+ spin_unlock(&chip->reg_lock);
+#if 0
+ snd_wss_debug(chip);
+#endif
+ return result;
+}
+
+/*
+ * CODEC I/O
+ */
+
+static unsigned char snd_wss_get_rate(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rates); i++)
+ if (rate == rates[i])
+ return freq_bits[i];
+ // snd_BUG();
+ return freq_bits[ARRAY_SIZE(rates) - 1];
+}
+
+static unsigned char snd_wss_get_format(struct snd_wss *chip,
+ int format,
+ int channels)
+{
+ unsigned char rformat;
+
+ rformat = CS4231_LINEAR_8;
+ switch (format) {
+ case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break;
+ case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break;
+ case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break;
+ case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break;
+ case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break;
+ }
+ if (channels > 1)
+ rformat |= CS4231_STEREO;
+#if 0
+ snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+#endif
+ return rformat;
+}
+
+static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
+{
+ unsigned long flags;
+
+ mute = mute ? 0x80 : 0;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->calibrate_mute == mute) {
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return;
+ }
+ if (!mute) {
+ snd_wss_dout(chip, CS4231_LEFT_INPUT,
+ chip->image[CS4231_LEFT_INPUT]);
+ snd_wss_dout(chip, CS4231_RIGHT_INPUT,
+ chip->image[CS4231_RIGHT_INPUT]);
+ snd_wss_dout(chip, CS4231_LOOPBACK,
+ chip->image[CS4231_LOOPBACK]);
+ }
+ snd_wss_dout(chip, CS4231_AUX1_LEFT_INPUT,
+ mute | chip->image[CS4231_AUX1_LEFT_INPUT]);
+ snd_wss_dout(chip, CS4231_AUX1_RIGHT_INPUT,
+ mute | chip->image[CS4231_AUX1_RIGHT_INPUT]);
+ snd_wss_dout(chip, CS4231_AUX2_LEFT_INPUT,
+ mute | chip->image[CS4231_AUX2_LEFT_INPUT]);
+ snd_wss_dout(chip, CS4231_AUX2_RIGHT_INPUT,
+ mute | chip->image[CS4231_AUX2_RIGHT_INPUT]);
+ snd_wss_dout(chip, CS4231_LEFT_OUTPUT,
+ mute | chip->image[CS4231_LEFT_OUTPUT]);
+ snd_wss_dout(chip, CS4231_RIGHT_OUTPUT,
+ mute | chip->image[CS4231_RIGHT_OUTPUT]);
+ if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
+ snd_wss_dout(chip, CS4231_LEFT_LINE_IN,
+ mute | chip->image[CS4231_LEFT_LINE_IN]);
+ snd_wss_dout(chip, CS4231_RIGHT_LINE_IN,
+ mute | chip->image[CS4231_RIGHT_LINE_IN]);
+ snd_wss_dout(chip, CS4231_MONO_CTRL,
+ mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
+ }
+ if (chip->hardware == WSS_HW_INTERWAVE) {
+ snd_wss_dout(chip, CS4231_LEFT_MIC_INPUT,
+ mute | chip->image[CS4231_LEFT_MIC_INPUT]);
+ snd_wss_dout(chip, CS4231_RIGHT_MIC_INPUT,
+ mute | chip->image[CS4231_RIGHT_MIC_INPUT]);
+ snd_wss_dout(chip, CS4231_LINE_LEFT_OUTPUT,
+ mute | chip->image[CS4231_LINE_LEFT_OUTPUT]);
+ snd_wss_dout(chip, CS4231_LINE_RIGHT_OUTPUT,
+ mute | chip->image[CS4231_LINE_RIGHT_OUTPUT]);
+ }
+ chip->calibrate_mute = mute;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_wss_playback_format(struct snd_wss *chip,
+ struct snd_pcm_hw_params *params,
+ unsigned char pdfr)
+{
+ unsigned long flags;
+ int full_calib = 1;
+
+ mutex_lock(&chip->mce_mutex);
+ snd_wss_calibrate_mute(chip, 1);
+ if (chip->hardware == WSS_HW_CS4231A ||
+ (chip->hardware & WSS_HW_CS4232_MASK)) {
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+ chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ chip->image[CS4231_PLAYBK_FORMAT]);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
+ udelay(100); /* Fixes audible clicks at least on GUS MAX */
+ full_calib = 0;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ }
+ if (full_calib) {
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
+ if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
+ pdfr = (pdfr & 0xf0) |
+ (chip->image[CS4231_REC_FORMAT] & 0x0f);
+ } else {
+ chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+ }
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (chip->hardware == WSS_HW_OPL3SA2)
+ udelay(100); /* this seems to help */
+ snd_wss_mce_down(chip);
+ }
+ snd_wss_calibrate_mute(chip, 0);
+ mutex_unlock(&chip->mce_mutex);
+}
+
+static void snd_wss_capture_format(struct snd_wss *chip,
+ struct snd_pcm_hw_params *params,
+ unsigned char cdfr)
+{
+ unsigned long flags;
+ int full_calib = 1;
+
+ mutex_lock(&chip->mce_mutex);
+ snd_wss_calibrate_mute(chip, 1);
+ if (chip->hardware == WSS_HW_CS4231A ||
+ (chip->hardware & WSS_HW_CS4232_MASK)) {
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */
+ (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+ snd_wss_out(chip, CS4231_REC_FORMAT,
+ chip->image[CS4231_REC_FORMAT] = cdfr);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
+ full_calib = 0;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ }
+ if (full_calib) {
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->hardware != WSS_HW_INTERWAVE &&
+ !(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+ if (chip->single_dma)
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr);
+ else
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ (chip->image[CS4231_PLAYBK_FORMAT] & 0xf0) |
+ (cdfr & 0x0f));
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ }
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr);
+ else
+ snd_wss_out(chip, CS4231_REC_FORMAT, cdfr);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+ }
+ snd_wss_calibrate_mute(chip, 0);
+ mutex_unlock(&chip->mce_mutex);
+}
+
+/*
+ * Timer interface
+ */
+
+static unsigned long snd_wss_timer_resolution(struct snd_timer *timer)
+{
+ struct snd_wss *chip = snd_timer_chip(timer);
+ if (chip->hardware & WSS_HW_CS4236B_MASK)
+ return 14467;
+ else
+ return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
+}
+
+static int snd_wss_timer_start(struct snd_timer *timer)
+{
+ unsigned long flags;
+ unsigned int ticks;
+ struct snd_wss *chip = snd_timer_chip(timer);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ ticks = timer->sticks;
+ if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
+ (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
+ (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
+ chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8);
+ snd_wss_out(chip, CS4231_TIMER_HIGH,
+ chip->image[CS4231_TIMER_HIGH]);
+ chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks;
+ snd_wss_out(chip, CS4231_TIMER_LOW,
+ chip->image[CS4231_TIMER_LOW]);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1] |
+ CS4231_TIMER_ENABLE);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_wss_timer_stop(struct snd_timer *timer)
+{
+ unsigned long flags;
+ struct snd_wss *chip = snd_timer_chip(timer);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+ chip->image[CS4231_ALT_FEATURE_1]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static void snd_wss_init(struct snd_wss *chip)
+{
+ unsigned long flags;
+
+ snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+ snd_printk("init: (1)\n");
+#endif
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
+ CS4231_PLAYBACK_PIO |
+ CS4231_RECORD_ENABLE |
+ CS4231_RECORD_PIO |
+ CS4231_CALIB_MODE);
+ chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+ snd_printk("init: (2)\n");
+#endif
+
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip,
+ CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+ snd_printk("init: (3) - afei = 0x%x\n",
+ chip->image[CS4231_ALT_FEATURE_1]);
+#endif
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip, CS4231_ALT_FEATURE_2,
+ chip->image[CS4231_ALT_FEATURE_2]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ chip->image[CS4231_PLAYBK_FORMAT]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+ snd_printk("init: (4)\n");
+#endif
+
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ snd_wss_out(chip, CS4231_REC_FORMAT,
+ chip->image[CS4231_REC_FORMAT]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+ snd_printk("init: (5)\n");
+#endif
+}
+
+static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
+{
+ unsigned long flags;
+
+ mutex_lock(&chip->open_mutex);
+ if ((chip->mode & mode) ||
+ ((chip->mode & WSS_MODE_OPEN) && chip->single_dma)) {
+ mutex_unlock(&chip->open_mutex);
+ return -EAGAIN;
+ }
+ if (chip->mode & WSS_MODE_OPEN) {
+ chip->mode |= mode;
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+ }
+ /* ok. now enable and ack CODEC IRQ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
+ snd_wss_out(chip, CS4231_IRQ_STATUS,
+ CS4231_PLAYBACK_IRQ |
+ CS4231_RECORD_IRQ |
+ CS4231_TIMER_IRQ);
+ snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+ }
+ wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
+ wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
+ chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
+ snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+ if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
+ snd_wss_out(chip, CS4231_IRQ_STATUS,
+ CS4231_PLAYBACK_IRQ |
+ CS4231_RECORD_IRQ |
+ CS4231_TIMER_IRQ);
+ snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ chip->mode = mode;
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
+{
+ unsigned long flags;
+
+ mutex_lock(&chip->open_mutex);
+ chip->mode &= ~mode;
+ if (chip->mode & WSS_MODE_OPEN) {
+ mutex_unlock(&chip->open_mutex);
+ return;
+ }
+ snd_wss_calibrate_mute(chip, 1);
+
+ /* disable IRQ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+ wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
+ wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
+ chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
+ snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+
+ /* now disable record & playback */
+
+ if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+ CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+ CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+ snd_wss_out(chip, CS4231_IFACE_CTRL,
+ chip->image[CS4231_IFACE_CTRL]);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_down(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ }
+
+ /* clear IRQ again */
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+ wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
+ wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ snd_wss_calibrate_mute(chip, 0);
+
+ chip->mode = 0;
+ mutex_unlock(&chip->open_mutex);
+}
+
+/*
+ * timer open/close
+ */
+
+static int snd_wss_timer_open(struct snd_timer *timer)
+{
+ struct snd_wss *chip = snd_timer_chip(timer);
+ snd_wss_open(chip, WSS_MODE_TIMER);
+ return 0;
+}
+
+static int snd_wss_timer_close(struct snd_timer *timer)
+{
+ struct snd_wss *chip = snd_timer_chip(timer);
+ snd_wss_close(chip, WSS_MODE_TIMER);
+ return 0;
+}
+
+static struct snd_timer_hardware snd_wss_timer_table =
+{
+ .flags = SNDRV_TIMER_HW_AUTO,
+ .resolution = 9945,
+ .ticks = 65535,
+ .open = snd_wss_timer_open,
+ .close = snd_wss_timer_close,
+ .c_resolution = snd_wss_timer_resolution,
+ .start = snd_wss_timer_start,
+ .stop = snd_wss_timer_stop,
+};
+
+/*
+ * ok.. exported functions..
+ */
+
+static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ unsigned char new_pdfr;
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ new_pdfr = snd_wss_get_format(chip, params_format(hw_params),
+ params_channels(hw_params)) |
+ snd_wss_get_rate(params_rate(hw_params));
+ chip->set_playback_format(chip, hw_params, new_pdfr);
+ return 0;
+}
+
+static int snd_wss_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_wss_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long flags;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->p_dma_size = size;
+ chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
+ snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+ count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
+ snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+ snd_wss_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+ snd_wss_debug(chip);
+#endif
+ return 0;
+}
+
+static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ unsigned char new_cdfr;
+ int err;
+
+ if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return err;
+ new_cdfr = snd_wss_get_format(chip, params_format(hw_params),
+ params_channels(hw_params)) |
+ snd_wss_get_rate(params_rate(hw_params));
+ chip->set_capture_format(chip, hw_params, new_cdfr);
+ return 0;
+}
+
+static int snd_wss_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_wss_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long flags;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->c_dma_size = size;
+ chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+ snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT],
+ count);
+ else
+ count = snd_wss_get_count(chip->image[CS4231_REC_FORMAT],
+ count);
+ count--;
+ if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) {
+ snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+ snd_wss_out(chip, CS4231_PLY_UPR_CNT,
+ (unsigned char) (count >> 8));
+ } else {
+ snd_wss_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
+ snd_wss_out(chip, CS4231_REC_UPR_CNT,
+ (unsigned char) (count >> 8));
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+void snd_wss_overrange(struct snd_wss *chip)
+{
+ unsigned long flags;
+ unsigned char res;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ res = snd_wss_in(chip, CS4231_TEST_INIT);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */
+ chip->capture_substream->runtime->overrange++;
+}
+EXPORT_SYMBOL(snd_wss_overrange);
+
+irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
+{
+ struct snd_wss *chip = dev_id;
+ unsigned char status;
+
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ /* pretend it was the only possible irq for AD1848 */
+ status = CS4231_PLAYBACK_IRQ;
+ else
+ status = snd_wss_in(chip, CS4231_IRQ_STATUS);
+ if (status & CS4231_TIMER_IRQ) {
+ if (chip->timer)
+ snd_timer_interrupt(chip->timer, chip->timer->sticks);
+ }
+ if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) {
+ if (status & CS4231_PLAYBACK_IRQ) {
+ if (chip->mode & WSS_MODE_PLAY) {
+ if (chip->playback_substream)
+ snd_pcm_period_elapsed(chip->playback_substream);
+ }
+ if (chip->mode & WSS_MODE_RECORD) {
+ if (chip->capture_substream) {
+ snd_wss_overrange(chip);
+ snd_pcm_period_elapsed(chip->capture_substream);
+ }
+ }
+ }
+ } else {
+ if (status & CS4231_PLAYBACK_IRQ) {
+ if (chip->playback_substream)
+ snd_pcm_period_elapsed(chip->playback_substream);
+ }
+ if (status & CS4231_RECORD_IRQ) {
+ if (chip->capture_substream) {
+ snd_wss_overrange(chip);
+ snd_pcm_period_elapsed(chip->capture_substream);
+ }
+ }
+ }
+
+ spin_lock(&chip->reg_lock);
+ status = ~CS4231_ALL_IRQS | ~status;
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ wss_outb(chip, CS4231P(STATUS), 0);
+ else
+ snd_wss_outm(chip, CS4231_IRQ_STATUS, status, 0);
+ spin_unlock(&chip->reg_lock);
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(snd_wss_interrupt);
+
+static snd_pcm_uframes_t snd_wss_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
+ return 0;
+ ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
+ return 0;
+ ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static int snd_ad1848_probe(struct snd_wss *chip)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ unsigned long flags;
+ unsigned char r;
+ unsigned short hardware = 0;
+ int err = 0;
+ int i;
+
+ while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
+ if (time_after(jiffies, timeout))
+ return -ENODEV;
+ cond_resched();
+ }
+ spin_lock_irqsave(&chip->reg_lock, flags);
+
+ /* set CS423x MODE 1 */
+ snd_wss_dout(chip, CS4231_MISC_INFO, 0);
+
+ snd_wss_dout(chip, CS4231_RIGHT_INPUT, 0x45); /* 0x55 & ~0x10 */
+ r = snd_wss_in(chip, CS4231_RIGHT_INPUT);
+ if (r != 0x45) {
+ /* RMGE always high on AD1847 */
+ if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45) {
+ err = -ENODEV;
+ goto out;
+ }
+ hardware = WSS_HW_AD1847;
+ } else {
+ snd_wss_dout(chip, CS4231_LEFT_INPUT, 0xaa);
+ r = snd_wss_in(chip, CS4231_LEFT_INPUT);
+ /* L/RMGE always low on AT2320 */
+ if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa) {
+ err = -ENODEV;
+ goto out;
+ }
+ }
+
+ /* clear pending IRQ */
+ wss_inb(chip, CS4231P(STATUS));
+ wss_outb(chip, CS4231P(STATUS), 0);
+ mb();
+
+ if ((chip->hardware & WSS_HW_TYPE_MASK) != WSS_HW_DETECT)
+ goto out;
+
+ if (hardware) {
+ chip->hardware = hardware;
+ goto out;
+ }
+
+ r = snd_wss_in(chip, CS4231_MISC_INFO);
+
+ /* set CS423x MODE 2 */
+ snd_wss_dout(chip, CS4231_MISC_INFO, CS4231_MODE2);
+ for (i = 0; i < 16; i++) {
+ if (snd_wss_in(chip, i) != snd_wss_in(chip, 16 + i)) {
+ /* we have more than 16 registers: check ID */
+ if ((r & 0xf) != 0xa)
+ goto out_mode;
+ /*
+ * on CMI8330, CS4231_VERSION is volume control and
+ * can be set to 0
+ */
+ snd_wss_dout(chip, CS4231_VERSION, 0);
+ r = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
+ if (!r)
+ chip->hardware = WSS_HW_CMI8330;
+ goto out_mode;
+ }
+ }
+ if (r & 0x80)
+ chip->hardware = WSS_HW_CS4248;
+ else
+ chip->hardware = WSS_HW_AD1848;
+out_mode:
+ snd_wss_dout(chip, CS4231_MISC_INFO, 0);
+out:
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return err;
+}
+
+static int snd_wss_probe(struct snd_wss *chip)
+{
+ unsigned long flags;
+ int i, id, rev, regnum;
+ unsigned char *ptr;
+ unsigned int hw;
+
+ id = snd_ad1848_probe(chip);
+ if (id < 0)
+ return id;
+
+ hw = chip->hardware;
+ if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
+ for (i = 0; i < 50; i++) {
+ mb();
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ msleep(2);
+ else {
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip, CS4231_MISC_INFO,
+ CS4231_MODE2);
+ id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (id == 0x0a)
+ break; /* this is valid value */
+ }
+ }
+ snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
+ if (id != 0x0a)
+ return -ENODEV; /* no valid device found */
+
+ rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
+ snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
+ if (rev == 0x80) {
+ unsigned char tmp = snd_wss_in(chip, 23);
+ snd_wss_out(chip, 23, ~tmp);
+ if (snd_wss_in(chip, 23) != tmp)
+ chip->hardware = WSS_HW_AD1845;
+ else
+ chip->hardware = WSS_HW_CS4231;
+ } else if (rev == 0xa0) {
+ chip->hardware = WSS_HW_CS4231A;
+ } else if (rev == 0xa2) {
+ chip->hardware = WSS_HW_CS4232;
+ } else if (rev == 0xb2) {
+ chip->hardware = WSS_HW_CS4232A;
+ } else if (rev == 0x83) {
+ chip->hardware = WSS_HW_CS4236;
+ } else if (rev == 0x03) {
+ chip->hardware = WSS_HW_CS4236B;
+ } else {
+ snd_printk("unknown CS chip with version 0x%x\n", rev);
+ return -ENODEV; /* unknown CS4231 chip? */
+ }
+ }
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ wss_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
+ wss_outb(chip, CS4231P(STATUS), 0);
+ mb();
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
+ switch (chip->hardware) {
+ case WSS_HW_INTERWAVE:
+ chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
+ break;
+ case WSS_HW_CS4235:
+ case WSS_HW_CS4236B:
+ case WSS_HW_CS4237B:
+ case WSS_HW_CS4238B:
+ case WSS_HW_CS4239:
+ if (hw == WSS_HW_DETECT3)
+ chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
+ else
+ chip->hardware = WSS_HW_CS4236;
+ break;
+ }
+
+ chip->image[CS4231_IFACE_CTRL] =
+ (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
+ (chip->single_dma ? CS4231_SINGLE_DMA : 0);
+ if (chip->hardware != WSS_HW_OPTI93X) {
+ chip->image[CS4231_ALT_FEATURE_1] = 0x80;
+ chip->image[CS4231_ALT_FEATURE_2] =
+ chip->hardware == WSS_HW_INTERWAVE ? 0xc2 : 0x01;
+ }
+ ptr = (unsigned char *) &chip->image;
+ regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
+ snd_wss_mce_down(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ for (i = 0; i < regnum; i++) /* ok.. fill all registers */
+ snd_wss_out(chip, i, *ptr++);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_wss_mce_up(chip);
+ snd_wss_mce_down(chip);
+
+ mdelay(2);
+
+ /* ok.. try check hardware version for CS4236+ chips */
+ if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
+ if (chip->hardware == WSS_HW_CS4236B) {
+ rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
+ snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
+ id = snd_cs4236_ext_in(chip, CS4236_VERSION);
+ snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
+ snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
+ if ((id & 0x1f) == 0x1d) { /* CS4235 */
+ chip->hardware = WSS_HW_CS4235;
+ switch (id >> 5) {
+ case 4:
+ case 5:
+ case 6:
+ break;
+ default:
+ snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
+ }
+ } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */
+ switch (id >> 5) {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ chip->hardware = WSS_HW_CS4236B;
+ break;
+ default:
+ snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
+ }
+ } else if ((id & 0x1f) == 0x08) { /* CS4237B */
+ chip->hardware = WSS_HW_CS4237B;
+ switch (id >> 5) {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ break;
+ default:
+ snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
+ }
+ } else if ((id & 0x1f) == 0x09) { /* CS4238B */
+ chip->hardware = WSS_HW_CS4238B;
+ switch (id >> 5) {
+ case 5:
+ case 6:
+ case 7:
+ break;
+ default:
+ snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
+ }
+ } else if ((id & 0x1f) == 0x1e) { /* CS4239 */
+ chip->hardware = WSS_HW_CS4239;
+ switch (id >> 5) {
+ case 4:
+ case 5:
+ case 6:
+ break;
+ default:
+ snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
+ }
+ } else {
+ snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
+ }
+ }
+ }
+ return 0; /* all things are ok.. */
+}
+
+/*
+
+ */
+
+static struct snd_pcm_hardware snd_wss_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5510,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware snd_wss_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+ .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 5510,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/*
+
+ */
+
+static int snd_wss_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ runtime->hw = snd_wss_playback;
+
+ /* hardware limitation of older chipsets */
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM |
+ SNDRV_PCM_FMTBIT_S16_BE);
+
+ /* hardware bug in InterWave chipset */
+ if (chip->hardware == WSS_HW_INTERWAVE && chip->dma1 > 3)
+ runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
+
+ /* hardware limitation of cheap chips */
+ if (chip->hardware == WSS_HW_CS4235 ||
+ chip->hardware == WSS_HW_CS4239)
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
+
+ snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+ snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+
+ if (chip->claim_dma) {
+ if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
+ return err;
+ }
+
+ err = snd_wss_open(chip, WSS_MODE_PLAY);
+ if (err < 0) {
+ if (chip->release_dma)
+ chip->release_dma(chip, chip->dma_private_data, chip->dma1);
+ snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+ return err;
+ }
+ chip->playback_substream = substream;
+ snd_pcm_set_sync(substream);
+ chip->rate_constraint(runtime);
+ return 0;
+}
+
+static int snd_wss_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ runtime->hw = snd_wss_capture;
+
+ /* hardware limitation of older chipsets */
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM |
+ SNDRV_PCM_FMTBIT_S16_BE);
+
+ /* hardware limitation of cheap chips */
+ if (chip->hardware == WSS_HW_CS4235 ||
+ chip->hardware == WSS_HW_CS4239 ||
+ chip->hardware == WSS_HW_OPTI93X)
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE;
+
+ snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+ snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+
+ if (chip->claim_dma) {
+ if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
+ return err;
+ }
+
+ err = snd_wss_open(chip, WSS_MODE_RECORD);
+ if (err < 0) {
+ if (chip->release_dma)
+ chip->release_dma(chip, chip->dma_private_data, chip->dma2);
+ snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+ return err;
+ }
+ chip->capture_substream = substream;
+ snd_pcm_set_sync(substream);
+ chip->rate_constraint(runtime);
+ return 0;
+}
+
+static int snd_wss_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+
+ chip->playback_substream = NULL;
+ snd_wss_close(chip, WSS_MODE_PLAY);
+ return 0;
+}
+
+static int snd_wss_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_wss *chip = snd_pcm_substream_chip(substream);
+
+ chip->capture_substream = NULL;
+ snd_wss_close(chip, WSS_MODE_RECORD);
+ return 0;
+}
+
+static void snd_wss_thinkpad_twiddle(struct snd_wss *chip, int on)
+{
+ int tmp;
+
+ if (!chip->thinkpad_flag)
+ return;
+
+ outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
+ tmp = inb(AD1848_THINKPAD_CTL_PORT2);
+
+ if (on)
+ /* turn it on */
+ tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
+ else
+ /* turn it off */
+ tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
+
+ outb(tmp, AD1848_THINKPAD_CTL_PORT2);
+}
+
+#ifdef CONFIG_PM
+
+/* lowlevel suspend callback for CS4231 */
+static void snd_wss_suspend(struct snd_wss *chip)
+{
+ int reg;
+ unsigned long flags;
+
+ snd_pcm_suspend_all(chip->pcm);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ for (reg = 0; reg < 32; reg++)
+ chip->image[reg] = snd_wss_in(chip, reg);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (chip->thinkpad_flag)
+ snd_wss_thinkpad_twiddle(chip, 0);
+}
+
+/* lowlevel resume callback for CS4231 */
+static void snd_wss_resume(struct snd_wss *chip)
+{
+ int reg;
+ unsigned long flags;
+ /* int timeout; */
+
+ if (chip->thinkpad_flag)
+ snd_wss_thinkpad_twiddle(chip, 1);
+ snd_wss_mce_up(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ for (reg = 0; reg < 32; reg++) {
+ switch (reg) {
+ case CS4231_VERSION:
+ break;
+ default:
+ snd_wss_out(chip, reg, chip->image[reg]);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 1
+ snd_wss_mce_down(chip);
+#else
+ /* The following is a workaround to avoid freeze after resume on TP600E.
+ This is the first half of copy of snd_wss_mce_down(), but doesn't
+ include rescheduling. -- iwai
+ */
+ snd_wss_busy_wait(chip);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->mce_bit &= ~CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (timeout == 0x80)
+ snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+ if ((timeout & CS4231_MCE) == 0 ||
+ !(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) {
+ return;
+ }
+ snd_wss_busy_wait(chip);
+#endif
+}
+#endif /* CONFIG_PM */
+
+static int snd_wss_free(struct snd_wss *chip)
+{
+ release_and_free_resource(chip->res_port);
+ release_and_free_resource(chip->res_cport);
+ if (chip->irq >= 0) {
+ disable_irq(chip->irq);
+ if (!(chip->hwshare & WSS_HWSHARE_IRQ))
+ free_irq(chip->irq, (void *) chip);
+ }
+ if (!(chip->hwshare & WSS_HWSHARE_DMA1) && chip->dma1 >= 0) {
+ snd_dma_disable(chip->dma1);
+ free_dma(chip->dma1);
+ }
+ if (!(chip->hwshare & WSS_HWSHARE_DMA2) &&
+ chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
+ snd_dma_disable(chip->dma2);
+ free_dma(chip->dma2);
+ }
+ if (chip->timer)
+ snd_device_free(chip->card, chip->timer);
+ kfree(chip);
+ return 0;
+}
+
+static int snd_wss_dev_free(struct snd_device *device)
+{
+ struct snd_wss *chip = device->device_data;
+ return snd_wss_free(chip);
+}
+
+const char *snd_wss_chip_id(struct snd_wss *chip)
+{
+ switch (chip->hardware) {
+ case WSS_HW_CS4231:
+ return "CS4231";
+ case WSS_HW_CS4231A:
+ return "CS4231A";
+ case WSS_HW_CS4232:
+ return "CS4232";
+ case WSS_HW_CS4232A:
+ return "CS4232A";
+ case WSS_HW_CS4235:
+ return "CS4235";
+ case WSS_HW_CS4236:
+ return "CS4236";
+ case WSS_HW_CS4236B:
+ return "CS4236B";
+ case WSS_HW_CS4237B:
+ return "CS4237B";
+ case WSS_HW_CS4238B:
+ return "CS4238B";
+ case WSS_HW_CS4239:
+ return "CS4239";
+ case WSS_HW_INTERWAVE:
+ return "AMD InterWave";
+ case WSS_HW_OPL3SA2:
+ return chip->card->shortname;
+ case WSS_HW_AD1845:
+ return "AD1845";
+ case WSS_HW_OPTI93X:
+ return "OPTi 93x";
+ case WSS_HW_AD1847:
+ return "AD1847";
+ case WSS_HW_AD1848:
+ return "AD1848";
+ case WSS_HW_CS4248:
+ return "CS4248";
+ case WSS_HW_CMI8330:
+ return "CMI8330/C3D";
+ default:
+ return "???";
+ }
+}
+EXPORT_SYMBOL(snd_wss_chip_id);
+
+static int snd_wss_new(struct snd_card *card,
+ unsigned short hardware,
+ unsigned short hwshare,
+ struct snd_wss **rchip)
+{
+ struct snd_wss *chip;
+
+ *rchip = NULL;
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ chip->hardware = hardware;
+ chip->hwshare = hwshare;
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->mce_mutex);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->rate_constraint = snd_wss_xrate;
+ chip->set_playback_format = snd_wss_playback_format;
+ chip->set_capture_format = snd_wss_capture_format;
+ if (chip->hardware == WSS_HW_OPTI93X)
+ memcpy(&chip->image, &snd_opti93x_original_image,
+ sizeof(snd_opti93x_original_image));
+ else
+ memcpy(&chip->image, &snd_wss_original_image,
+ sizeof(snd_wss_original_image));
+ if (chip->hardware & WSS_HW_AD1848_MASK) {
+ chip->image[CS4231_PIN_CTRL] = 0;
+ chip->image[CS4231_TEST_INIT] = 0;
+ }
+
+ *rchip = chip;
+ return 0;
+}
+
+int snd_wss_create(struct snd_card *card,
+ unsigned long port,
+ unsigned long cport,
+ int irq, int dma1, int dma2,
+ unsigned short hardware,
+ unsigned short hwshare,
+ struct snd_wss **rchip)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = snd_wss_dev_free,
+ };
+ struct snd_wss *chip;
+ int err;
+
+ err = snd_wss_new(card, hardware, hwshare, &chip);
+ if (err < 0)
+ return err;
+
+ chip->irq = -1;
+ chip->dma1 = -1;
+ chip->dma2 = -1;
+
+ chip->res_port = request_region(port, 4, "WSS");
+ if (!chip->res_port) {
+ snd_printk(KERN_ERR "wss: can't grab port 0x%lx\n", port);
+ snd_wss_free(chip);
+ return -EBUSY;
+ }
+ chip->port = port;
+ if ((long)cport >= 0) {
+ chip->res_cport = request_region(cport, 8, "CS4232 Control");
+ if (!chip->res_cport) {
+ snd_printk(KERN_ERR
+ "wss: can't grab control port 0x%lx\n", cport);
+ snd_wss_free(chip);
+ return -ENODEV;
+ }
+ }
+ chip->cport = cport;
+ if (!(hwshare & WSS_HWSHARE_IRQ))
+ if (request_irq(irq, snd_wss_interrupt, IRQF_DISABLED,
+ "WSS", (void *) chip)) {
+ snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
+ snd_wss_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = irq;
+ if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "WSS - 1")) {
+ snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1);
+ snd_wss_free(chip);
+ return -EBUSY;
+ }
+ chip->dma1 = dma1;
+ if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 &&
+ dma2 >= 0 && request_dma(dma2, "WSS - 2")) {
+ snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2);
+ snd_wss_free(chip);
+ return -EBUSY;
+ }
+ if (dma1 == dma2 || dma2 < 0) {
+ chip->single_dma = 1;
+ chip->dma2 = chip->dma1;
+ } else
+ chip->dma2 = dma2;
+
+ if (hardware == WSS_HW_THINKPAD) {
+ chip->thinkpad_flag = 1;
+ chip->hardware = WSS_HW_DETECT; /* reset */
+ snd_wss_thinkpad_twiddle(chip, 1);
+ }
+
+ /* global setup */
+ if (snd_wss_probe(chip) < 0) {
+ snd_wss_free(chip);
+ return -ENODEV;
+ }
+ snd_wss_init(chip);
+
+#if 0
+ if (chip->hardware & WSS_HW_CS4232_MASK) {
+ if (chip->res_cport == NULL)
+ snd_printk("CS4232 control port features are not accessible\n");
+ }
+#endif
+
+ /* Register device */
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_wss_free(chip);
+ return err;
+ }
+
+#ifdef CONFIG_PM
+ /* Power Management */
+ chip->suspend = snd_wss_suspend;
+ chip->resume = snd_wss_resume;
+#endif
+
+ *rchip = chip;
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_create);
+
+static struct snd_pcm_ops snd_wss_playback_ops = {
+ .open = snd_wss_playback_open,
+ .close = snd_wss_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_wss_playback_hw_params,
+ .hw_free = snd_wss_playback_hw_free,
+ .prepare = snd_wss_playback_prepare,
+ .trigger = snd_wss_trigger,
+ .pointer = snd_wss_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_wss_capture_ops = {
+ .open = snd_wss_capture_open,
+ .close = snd_wss_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_wss_capture_hw_params,
+ .hw_free = snd_wss_capture_hw_free,
+ .prepare = snd_wss_capture_prepare,
+ .trigger = snd_wss_trigger,
+ .pointer = snd_wss_capture_pointer,
+};
+
+int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(chip->card, "WSS", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_wss_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_wss_capture_ops);
+
+ /* global setup */
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ if (chip->single_dma)
+ pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+ if (chip->hardware != WSS_HW_INTERWAVE)
+ pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ strcpy(pcm->name, snd_wss_chip_id(chip));
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_isa_data(),
+ 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+
+ chip->pcm = pcm;
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_pcm);
+
+static void snd_wss_timer_free(struct snd_timer *timer)
+{
+ struct snd_wss *chip = timer->private_data;
+ chip->timer = NULL;
+}
+
+int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer)
+{
+ struct snd_timer *timer;
+ struct snd_timer_id tid;
+ int err;
+
+ /* Timer initialization */
+ tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+ tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+ tid.card = chip->card->number;
+ tid.device = device;
+ tid.subdevice = 0;
+ if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
+ return err;
+ strcpy(timer->name, snd_wss_chip_id(chip));
+ timer->private_data = chip;
+ timer->private_free = snd_wss_timer_free;
+ timer->hw = snd_wss_timer_table;
+ chip->timer = timer;
+ if (rtimer)
+ *rtimer = timer;
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_timer);
+
+/*
+ * MIXER part
+ */
+
+static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[4] = {
+ "Line", "Aux", "Mic", "Mix"
+ };
+ static char *opl3sa_texts[4] = {
+ "Line", "CD", "Mic", "Mix"
+ };
+ static char *gusmax_texts[4] = {
+ "Line", "Synth", "Mic", "Mix"
+ };
+ char **ptexts = texts;
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+
+ if (snd_BUG_ON(!chip->card))
+ return -EINVAL;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 2;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item > 3)
+ uinfo->value.enumerated.item = 3;
+ if (!strcmp(chip->card->driver, "GUS MAX"))
+ ptexts = gusmax_texts;
+ switch (chip->hardware) {
+ case WSS_HW_INTERWAVE:
+ ptexts = gusmax_texts;
+ break;
+ case WSS_HW_OPL3SA2:
+ ptexts = opl3sa_texts;
+ break;
+ }
+ strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
+ ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ unsigned short left, right;
+ int change;
+
+ if (ucontrol->value.enumerated.item[0] > 3 ||
+ ucontrol->value.enumerated.item[1] > 3)
+ return -EINVAL;
+ left = ucontrol->value.enumerated.item[0] << 6;
+ right = ucontrol->value.enumerated.item[1] << 6;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
+ right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
+ change = left != chip->image[CS4231_LEFT_INPUT] ||
+ right != chip->image[CS4231_RIGHT_INPUT];
+ snd_wss_out(chip, CS4231_LEFT_INPUT, left);
+ snd_wss_out(chip, CS4231_RIGHT_INPUT, right);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return change;
+}
+
+int snd_wss_info_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_info_single);
+
+int snd_wss_get_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (invert)
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_get_single);
+
+int snd_wss_put_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & 0xff;
+ int change;
+ unsigned short val;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = mask - val;
+ val <<= shift;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ val = (chip->image[reg] & ~(mask << shift)) | val;
+ change = val != chip->image[reg];
+ snd_wss_out(chip, reg, val);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return change;
+}
+EXPORT_SYMBOL(snd_wss_put_single);
+
+int snd_wss_info_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_info_double);
+
+int snd_wss_get_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x07;
+ int shift_right = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+ ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (invert) {
+ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ }
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_get_double);
+
+int snd_wss_put_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x07;
+ int shift_right = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 22) & 1;
+ int change;
+ unsigned short val1, val2;
+
+ val1 = ucontrol->value.integer.value[0] & mask;
+ val2 = ucontrol->value.integer.value[1] & mask;
+ if (invert) {
+ val1 = mask - val1;
+ val2 = mask - val2;
+ }
+ val1 <<= shift_left;
+ val2 <<= shift_right;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (left_reg != right_reg) {
+ val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+ val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+ change = val1 != chip->image[left_reg] ||
+ val2 != chip->image[right_reg];
+ snd_wss_out(chip, left_reg, val1);
+ snd_wss_out(chip, right_reg, val2);
+ } else {
+ mask = (mask << shift_left) | (mask << shift_right);
+ val1 = (chip->image[left_reg] & ~mask) | val1 | val2;
+ change = val1 != chip->image[left_reg];
+ snd_wss_out(chip, left_reg, val1);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return change;
+}
+EXPORT_SYMBOL(snd_wss_put_double);
+
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
+static struct snd_kcontrol_new snd_ad1848_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT,
+ 7, 7, 1, 1),
+WSS_DOUBLE_TLV("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
+WSS_DOUBLE("Aux Playback Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Aux Playback Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
+WSS_DOUBLE("Aux Playback Switch", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Aux Playback Volume", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
+WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
+ 0, 0, 15, 0, db_scale_rec_gain),
+{
+ .name = "Capture Source",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_wss_info_mux,
+ .get = snd_wss_get_mux,
+ .put = snd_wss_put_mux,
+},
+WSS_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 1, 63, 0,
+ db_scale_6bit),
+};
+
+static struct snd_kcontrol_new snd_wss_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Line Playback Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE("Aux Playback Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Aux Playback Switch", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_SINGLE("Mono Playback Switch", 0,
+ CS4231_MONO_CTRL, 7, 1, 1),
+WSS_SINGLE("Mono Playback Volume", 0,
+ CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Mono Output Playback Switch", 0,
+ CS4231_MONO_CTRL, 6, 1, 1),
+WSS_SINGLE("Mono Output Playback Bypass", 0,
+ CS4231_MONO_CTRL, 5, 1, 0),
+WSS_DOUBLE("Capture Volume", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_wss_info_mux,
+ .get = snd_wss_get_mux,
+ .put = snd_wss_put_mux,
+},
+WSS_DOUBLE("Mic Boost", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_SINGLE("Loopback Capture Switch", 0,
+ CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE("Loopback Capture Volume", 0,
+ CS4231_LOOPBACK, 2, 63, 1)
+};
+
+static struct snd_kcontrol_new snd_opti93x_controls[] = {
+WSS_DOUBLE("Master Playback Switch", 0,
+ OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
+WSS_DOUBLE("Master Playback Volume", 0,
+ OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
+WSS_DOUBLE("PCM Playback Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1),
+WSS_DOUBLE("FM Playback Switch", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("FM Playback Volume", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Line Playback Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1),
+WSS_DOUBLE("Mic Playback Switch", 0,
+ OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Mic Playback Volume", 0,
+ OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Mic Boost", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_DOUBLE("CD Playback Switch", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("CD Playback Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Aux Playback Switch", 0,
+ OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 0,
+ OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Capture Volume", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_wss_info_mux,
+ .get = snd_wss_get_mux,
+ .put = snd_wss_put_mux,
+}
+};
+
+int snd_wss_mixer(struct snd_wss *chip)
+{
+ struct snd_card *card;
+ unsigned int idx;
+ int err;
+
+ if (snd_BUG_ON(!chip || !chip->pcm))
+ return -EINVAL;
+
+ card = chip->card;
+
+ strcpy(card->mixername, chip->pcm->name);
+
+ if (chip->hardware == WSS_HW_OPTI93X)
+ for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_opti93x_controls[idx],
+ chip));
+ if (err < 0)
+ return err;
+ }
+ else if (chip->hardware & WSS_HW_AD1848_MASK)
+ for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_ad1848_controls[idx],
+ chip));
+ if (err < 0)
+ return err;
+ }
+ else
+ for (idx = 0; idx < ARRAY_SIZE(snd_wss_controls); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_wss_controls[idx],
+ chip));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(snd_wss_mixer);
+
+const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction)
+{
+ return direction == SNDRV_PCM_STREAM_PLAYBACK ?
+ &snd_wss_playback_ops : &snd_wss_capture_ops;
+}
+EXPORT_SYMBOL(snd_wss_get_pcm_ops);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_wss_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_wss_exit(void)
+{
+}
+
+module_init(alsa_wss_init);
+module_exit(alsa_wss_exit);
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index 531f8ba96a7..a9823fad85c 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -1,15 +1,34 @@
# ALSA MIPS drivers
-menu "ALSA MIPS devices"
- depends on SND!=n && MIPS
+menuconfig SND_MIPS
+ bool "MIPS sound devices"
+ depends on MIPS
+ default y
+ help
+ Support for sound devices of MIPS architectures.
+
+if SND_MIPS
+
+config SND_SGI_O2
+ tristate "SGI O2 Audio"
+ depends on SGI_IP32
+ help
+ Sound support for the SGI O2 Workstation.
+
+config SND_SGI_HAL2
+ tristate "SGI HAL2 Audio"
+ depends on SGI_HAS_HAL2
+ help
+ Sound support for the SGI Indy and Indigo2 Workstation.
+
config SND_AU1X00
tristate "Au1x00 AC97 Port Driver"
- depends on (SOC_AU1000 || SOC_AU1100 || SOC_AU1500) && SND
+ depends on SOC_AU1000 || SOC_AU1100 || SOC_AU1500
select SND_PCM
select SND_AC97_CODEC
help
ALSA Sound driver for the Au1x00's AC97 port.
-endmenu
+endif # SND_MIPS
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 47afed971fb..861ec0a574b 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -3,6 +3,10 @@
#
snd-au1x00-objs := au1x00.o
+snd-sgi-o2-objs := sgio2audio.o ad1843.o
+snd-sgi-hal2-objs := hal2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
+obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
+obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c
new file mode 100644
index 00000000000..c624510ec37
--- /dev/null
+++ b/sound/mips/ad1843.c
@@ -0,0 +1,561 @@
+/*
+ * AD1843 low level driver
+ *
+ * Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ * Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *
+ * inspired from vwsnd.c (SGI VW audio driver)
+ * Copyright 1999 Silicon Graphics, Inc. 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; 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/sched.h>
+#include <linux/errno.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ad1843.h>
+
+/*
+ * AD1843 bitfield definitions. All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+struct ad1843_bitfield {
+ char reg;
+ char lo_bit;
+ char nbits;
+};
+
+static const struct ad1843_bitfield
+ ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */
+ ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */
+ ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */
+ ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */
+ ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */
+ ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */
+ ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */
+ ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */
+ ad1843_RD2M = { 3, 0, 5 }, /* Right DAC 2 Mix Gain/Atten */
+ ad1843_RD2MM = { 3, 7, 1 }, /* Right DAC 2 Mix Mute */
+ ad1843_LD2M = { 3, 8, 5 }, /* Left DAC 2 Mix Gain/Atten */
+ ad1843_LD2MM = { 3, 15, 1 }, /* Left DAC 2 Mix Mute */
+ ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */
+ ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */
+ ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */
+ ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */
+ ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */
+ ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */
+ ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */
+ ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */
+ ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */
+ ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */
+ ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */
+ ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */
+ ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */
+ ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */
+ ad1843_MPOM = { 8, 6, 1 }, /* Mono Output Mute */
+ ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */
+ ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */
+ ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */
+ ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */
+ ad1843_RDA2G = { 10, 0, 6 }, /* Right DAC2 Analog/Digital Gain */
+ ad1843_RDA2GM = { 10, 7, 1 }, /* Right DAC2 Analog Mute */
+ ad1843_LDA2G = { 10, 8, 6 }, /* Left DAC2 Analog/Digital Gain */
+ ad1843_LDA2GM = { 10, 15, 1 }, /* Left DAC2 Analog Mute */
+ ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */
+ ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */
+ ad1843_RDA2AM = { 12, 7, 1 }, /* Right DAC2 Digital Mute */
+ ad1843_LDA2AM = { 12, 15, 1 }, /* Left DAC2 Digital Mute */
+ ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */
+ ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */
+ ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */
+ ad1843_DA2C = { 15, 10, 2 }, /* DAC2 Sample Rate Source */
+ ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */
+ ad1843_C2C = { 20, 0, 16 }, /* Clock 2 Sample Rate Select */
+ ad1843_C3C = { 23, 0, 16 }, /* Clock 3 Sample Rate Select */
+ ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */
+ ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */
+ ad1843_DAMIX = { 25, 14, 1 }, /* DAC Digital Mix Enable */
+ ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */
+ ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */
+ ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */
+ ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */
+ ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */
+ ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */
+ ad1843_DA2F = { 26, 10, 2 }, /* DAC2 Data Format Select */
+ ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */
+ ad1843_DA2SM = { 26, 15, 1 }, /* DAC2 Stereo/Mono Mode Select */
+ ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */
+ ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */
+ ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */
+ ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */
+ ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */
+ ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */
+ ad1843_DDMEN = { 27, 12, 1 }, /* DAC2 to DAC1 Mix Enable */
+ ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */
+ ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */
+ ad1843_C3EN = { 28, 13, 1 }, /* Clock Generator 3 Enable */
+ ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain. The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+struct ad1843_gain {
+ int negative; /* nonzero if gain is negative. */
+ const struct ad1843_bitfield *lfield;
+ const struct ad1843_bitfield *rfield;
+ const struct ad1843_bitfield *lmute;
+ const struct ad1843_bitfield *rmute;
+};
+
+static const struct ad1843_gain ad1843_gain_RECLEV = {
+ .negative = 0,
+ .lfield = &ad1843_LIG,
+ .rfield = &ad1843_RIG
+};
+static const struct ad1843_gain ad1843_gain_LINE = {
+ .negative = 1,
+ .lfield = &ad1843_LX1M,
+ .rfield = &ad1843_RX1M,
+ .lmute = &ad1843_LX1MM,
+ .rmute = &ad1843_RX1MM
+};
+static const struct ad1843_gain ad1843_gain_LINE_2 = {
+ .negative = 1,
+ .lfield = &ad1843_LDA2G,
+ .rfield = &ad1843_RDA2G,
+ .lmute = &ad1843_LDA2GM,
+ .rmute = &ad1843_RDA2GM
+};
+static const struct ad1843_gain ad1843_gain_MIC = {
+ .negative = 1,
+ .lfield = &ad1843_LMCM,
+ .rfield = &ad1843_RMCM,
+ .lmute = &ad1843_LMCMM,
+ .rmute = &ad1843_RMCMM
+};
+static const struct ad1843_gain ad1843_gain_PCM_0 = {
+ .negative = 1,
+ .lfield = &ad1843_LDA1G,
+ .rfield = &ad1843_RDA1G,
+ .lmute = &ad1843_LDA1GM,
+ .rmute = &ad1843_RDA1GM
+};
+static const struct ad1843_gain ad1843_gain_PCM_1 = {
+ .negative = 1,
+ .lfield = &ad1843_LD2M,
+ .rfield = &ad1843_RD2M,
+ .lmute = &ad1843_LD2MM,
+ .rmute = &ad1843_RD2MM
+};
+
+static const struct ad1843_gain *ad1843_gain[AD1843_GAIN_SIZE] =
+{
+ &ad1843_gain_RECLEV,
+ &ad1843_gain_LINE,
+ &ad1843_gain_LINE_2,
+ &ad1843_gain_MIC,
+ &ad1843_gain_PCM_0,
+ &ad1843_gain_PCM_1,
+};
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(struct snd_ad1843 *ad1843,
+ const struct ad1843_bitfield *field)
+{
+ int w;
+
+ w = ad1843->read(ad1843->chip, field->reg);
+ return w >> field->lo_bit & ((1 << field->nbits) - 1);
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(struct snd_ad1843 *ad1843,
+ const struct ad1843_bitfield *field,
+ int newval)
+{
+ int w, mask, oldval, newbits;
+
+ w = ad1843->read(ad1843->chip, field->reg);
+ mask = ((1 << field->nbits) - 1) << field->lo_bit;
+ oldval = (w & mask) >> field->lo_bit;
+ newbits = (newval << field->lo_bit) & mask;
+ w = (w & ~mask) | newbits;
+ ad1843->write(ad1843->chip, field->reg, w);
+
+ return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register. It uses a single read cycle to do it. (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called like this.
+ *
+ * ad1843_read_multi(ad1843, nfields,
+ * &ad1843_FIELD1, &val1,
+ * &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(struct snd_ad1843 *ad1843, int argcount, ...)
+{
+ va_list ap;
+ const struct ad1843_bitfield *fp;
+ int w = 0, mask, *value, reg = -1;
+
+ va_start(ap, argcount);
+ while (--argcount >= 0) {
+ fp = va_arg(ap, const struct ad1843_bitfield *);
+ value = va_arg(ap, int *);
+ if (reg == -1) {
+ reg = fp->reg;
+ w = ad1843->read(ad1843->chip, reg);
+ }
+
+ mask = (1 << fp->nbits) - 1;
+ *value = w >> fp->lo_bit & mask;
+ }
+ va_end(ap);
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register. It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ * ad1843_write_multi(ad1843, nfields,
+ * &ad1843_FIELD1, val1,
+ * &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
+{
+ va_list ap;
+ int reg;
+ const struct ad1843_bitfield *fp;
+ int value;
+ int w, m, mask, bits;
+
+ mask = 0;
+ bits = 0;
+ reg = -1;
+
+ va_start(ap, argcount);
+ while (--argcount >= 0) {
+ fp = va_arg(ap, const struct ad1843_bitfield *);
+ value = va_arg(ap, int);
+ if (reg == -1)
+ reg = fp->reg;
+ else
+ BUG_ON(reg != fp->reg);
+ m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+ mask |= m;
+ bits |= (value << fp->lo_bit) & m;
+ }
+ va_end(ap);
+
+ if (~mask & 0xFFFF)
+ w = ad1843->read(ad1843->chip, reg);
+ else
+ w = 0;
+ w = (w & ~mask) | bits;
+ ad1843->write(ad1843->chip, reg, w);
+}
+
+int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id)
+{
+ const struct ad1843_gain *gp = ad1843_gain[id];
+ int ret;
+
+ ret = (1 << gp->lfield->nbits);
+ if (!gp->lmute)
+ ret -= 1;
+ return ret;
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.
+ */
+
+int ad1843_get_gain(struct snd_ad1843 *ad1843, int id)
+{
+ int lg, rg, lm, rm;
+ const struct ad1843_gain *gp = ad1843_gain[id];
+ unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+ ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg);
+ if (gp->negative) {
+ lg = mask - lg;
+ rg = mask - rg;
+ }
+ if (gp->lmute) {
+ ad1843_read_multi(ad1843, 2, gp->lmute, &lm, gp->rmute, &rm);
+ if (lm)
+ lg = 0;
+ if (rm)
+ rg = 0;
+ }
+ return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval)
+{
+ const struct ad1843_gain *gp = ad1843_gain[id];
+ unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+ int lg = (newval >> 0) & mask;
+ int rg = (newval >> 8) & mask;
+ int lm = (lg == 0) ? 1 : 0;
+ int rm = (rg == 0) ? 1 : 0;
+
+ if (gp->negative) {
+ lg = mask - lg;
+ rg = mask - rg;
+ }
+ if (gp->lmute)
+ ad1843_write_multi(ad1843, 2, gp->lmute, lm, gp->rmute, rm);
+ ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg);
+ return ad1843_get_gain(ad1843, id);
+}
+
+/* Returns the current recording source */
+
+int ad1843_get_recsrc(struct snd_ad1843 *ad1843)
+{
+ int val = ad1843_read_bits(ad1843, &ad1843_LSS);
+
+ if (val < 0 || val > 2) {
+ val = 2;
+ ad1843_write_multi(ad1843, 2,
+ &ad1843_LSS, val, &ad1843_RSS, val);
+ }
+ return val;
+}
+
+/*
+ * Set recording source.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc)
+{
+ if (newsrc < 0 || newsrc > 2)
+ return -EINVAL;
+
+ ad1843_write_multi(ad1843, 2, &ad1843_LSS, newsrc, &ad1843_RSS, newsrc);
+ return newsrc;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+void ad1843_setup_dac(struct snd_ad1843 *ad1843,
+ unsigned int id,
+ unsigned int framerate,
+ snd_pcm_format_t fmt,
+ unsigned int channels)
+{
+ int ad_fmt = 0, ad_mode = 0;
+
+ switch (fmt) {
+ case SNDRV_PCM_FORMAT_S8:
+ ad_fmt = 0;
+ break;
+ case SNDRV_PCM_FORMAT_U8:
+ ad_fmt = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ad_fmt = 1;
+ break;
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ ad_fmt = 2;
+ break;
+ case SNDRV_PCM_FORMAT_A_LAW:
+ ad_fmt = 3;
+ break;
+ default:
+ break;
+ }
+
+ switch (channels) {
+ case 2:
+ ad_mode = 0;
+ break;
+ case 1:
+ ad_mode = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (id) {
+ ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
+ ad1843_write_multi(ad1843, 2,
+ &ad1843_DA2SM, ad_mode,
+ &ad1843_DA2F, ad_fmt);
+ } else {
+ ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
+ ad1843_write_multi(ad1843, 2,
+ &ad1843_DA1SM, ad_mode,
+ &ad1843_DA1F, ad_fmt);
+ }
+}
+
+void ad1843_shutdown_dac(struct snd_ad1843 *ad1843, unsigned int id)
+{
+ if (id)
+ ad1843_write_bits(ad1843, &ad1843_DA2F, 1);
+ else
+ ad1843_write_bits(ad1843, &ad1843_DA1F, 1);
+}
+
+void ad1843_setup_adc(struct snd_ad1843 *ad1843,
+ unsigned int framerate,
+ snd_pcm_format_t fmt,
+ unsigned int channels)
+{
+ int da_fmt = 0;
+
+ switch (fmt) {
+ case SNDRV_PCM_FORMAT_S8: da_fmt = 0; break;
+ case SNDRV_PCM_FORMAT_U8: da_fmt = 0; break;
+ case SNDRV_PCM_FORMAT_S16_LE: da_fmt = 1; break;
+ case SNDRV_PCM_FORMAT_MU_LAW: da_fmt = 2; break;
+ case SNDRV_PCM_FORMAT_A_LAW: da_fmt = 3; break;
+ default: break;
+ }
+
+ ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
+ ad1843_write_multi(ad1843, 2,
+ &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+void ad1843_shutdown_adc(struct snd_ad1843 *ad1843)
+{
+ /* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843. As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE". The numbered comments are
+ * subsection headings from the data sheet. See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure. */
+
+int ad1843_init(struct snd_ad1843 *ad1843)
+{
+ unsigned long later;
+
+ if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) {
+ printk(KERN_ERR "ad1843: AD1843 won't initialize\n");
+ return -EIO;
+ }
+
+ ad1843_write_bits(ad1843, &ad1843_SCF, 1);
+
+ /* 4. Put the conversion resources into standby. */
+ ad1843_write_bits(ad1843, &ad1843_PDNI, 0);
+ later = jiffies + msecs_to_jiffies(500);
+
+ while (ad1843_read_bits(ad1843, &ad1843_PDNO)) {
+ if (time_after(jiffies, later)) {
+ printk(KERN_ERR
+ "ad1843: AD1843 won't power up\n");
+ return -EIO;
+ }
+ schedule_timeout_interruptible(5);
+ }
+
+ /* 5. Power up the clock generators and enable clock output pins. */
+ ad1843_write_multi(ad1843, 3,
+ &ad1843_C1EN, 1,
+ &ad1843_C2EN, 1,
+ &ad1843_C3EN, 1);
+
+ /* 6. Configure conversion resources while they are in standby. */
+
+ /* DAC1/2 use clock 1/2 as source, ADC uses clock 3. Always. */
+ ad1843_write_multi(ad1843, 4,
+ &ad1843_DA1C, 1,
+ &ad1843_DA2C, 2,
+ &ad1843_ADLC, 3,
+ &ad1843_ADRC, 3);
+
+ /* 7. Enable conversion resources. */
+ ad1843_write_bits(ad1843, &ad1843_ADTLK, 1);
+ ad1843_write_multi(ad1843, 7,
+ &ad1843_ANAEN, 1,
+ &ad1843_AAMEN, 1,
+ &ad1843_DA1EN, 1,
+ &ad1843_DA2EN, 1,
+ &ad1843_DDMEN, 1,
+ &ad1843_ADLEN, 1,
+ &ad1843_ADREN, 1);
+
+ /* 8. Configure conversion resources while they are enabled. */
+
+ /* set gain to 0 for all channels */
+ ad1843_set_gain(ad1843, AD1843_GAIN_RECLEV, 0);
+ ad1843_set_gain(ad1843, AD1843_GAIN_LINE, 0);
+ ad1843_set_gain(ad1843, AD1843_GAIN_LINE_2, 0);
+ ad1843_set_gain(ad1843, AD1843_GAIN_MIC, 0);
+ ad1843_set_gain(ad1843, AD1843_GAIN_PCM_0, 0);
+ ad1843_set_gain(ad1843, AD1843_GAIN_PCM_1, 0);
+
+ /* Unmute all channels. */
+ /* DAC1 */
+ ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0);
+ /* DAC2 */
+ ad1843_write_multi(ad1843, 2, &ad1843_LDA2GM, 0, &ad1843_RDA2GM, 0);
+
+ /* Set default recording source to Line In and set
+ * mic gain to +20 dB.
+ */
+ ad1843_set_recsrc(ad1843, 2);
+ ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+ /* Set Speaker Out level to +/- 4V and unmute it. */
+ ad1843_write_multi(ad1843, 3,
+ &ad1843_HPOS, 1,
+ &ad1843_HPOM, 0,
+ &ad1843_MPOM, 0);
+
+ return 0;
+}
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
index ee0741f9eb5..1881cec11e7 100644
--- a/sound/mips/au1x00.c
+++ b/sound/mips/au1x00.c
@@ -38,7 +38,6 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/version.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -191,14 +190,16 @@ au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
static void
au1000_dma_stop(struct audio_stream *stream)
{
- snd_assert(stream->buffer, return);
+ if (snd_BUG_ON(!stream->buffer))
+ return;
disable_dma(stream->dma);
}
static void
au1000_dma_start(struct audio_stream *stream)
{
- snd_assert(stream->buffer, return);
+ if (snd_BUG_ON(!stream->buffer))
+ return;
init_dma(stream->dma);
if (get_dma_active_buffer(stream->dma) == 0) {
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
new file mode 100644
index 00000000000..db495be0186
--- /dev/null
+++ b/sound/mips/hal2.c
@@ -0,0 +1,947 @@
+/*
+ * Driver for A2 audio system used in SGI machines
+ * Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
+ *
+ * Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
+ * was based on code from Ulf Carlsson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm-indirect.h>
+#include <sound/initval.h>
+
+#include "hal2.h"
+
+static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SGI HAL2 soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SGI HAL2 soundcard.");
+MODULE_DESCRIPTION("ALSA driver for SGI HAL2 audio");
+MODULE_AUTHOR("Thomas Bogendoerfer");
+MODULE_LICENSE("GPL");
+
+
+#define H2_BLOCK_SIZE 1024
+#define H2_BUF_SIZE 16384
+
+struct hal2_pbus {
+ struct hpc3_pbus_dmacregs *pbus;
+ int pbusnr;
+ unsigned int ctrl; /* Current state of pbus->pbdma_ctrl */
+};
+
+struct hal2_desc {
+ struct hpc_dma_desc desc;
+ u32 pad; /* padding */
+};
+
+struct hal2_codec {
+ struct snd_pcm_indirect pcm_indirect;
+ struct snd_pcm_substream *substream;
+
+ unsigned char *buffer;
+ dma_addr_t buffer_dma;
+ struct hal2_desc *desc;
+ dma_addr_t desc_dma;
+ int desc_count;
+ struct hal2_pbus pbus;
+ int voices; /* mono/stereo */
+ unsigned int sample_rate;
+ unsigned int master; /* Master frequency */
+ unsigned short mod; /* MOD value */
+ unsigned short inc; /* INC value */
+};
+
+#define H2_MIX_OUTPUT_ATT 0
+#define H2_MIX_INPUT_GAIN 1
+
+struct snd_hal2 {
+ struct snd_card *card;
+
+ struct hal2_ctl_regs *ctl_regs; /* HAL2 ctl registers */
+ struct hal2_aes_regs *aes_regs; /* HAL2 aes registers */
+ struct hal2_vol_regs *vol_regs; /* HAL2 vol registers */
+ struct hal2_syn_regs *syn_regs; /* HAL2 syn registers */
+
+ struct hal2_codec dac;
+ struct hal2_codec adc;
+};
+
+#define H2_INDIRECT_WAIT(regs) while (hal2_read(&regs->isr) & H2_ISR_TSTATUS);
+
+#define H2_READ_ADDR(addr) (addr | (1<<7))
+#define H2_WRITE_ADDR(addr) (addr)
+
+static inline u32 hal2_read(u32 *reg)
+{
+ return __raw_readl(reg);
+}
+
+static inline void hal2_write(u32 val, u32 *reg)
+{
+ __raw_writel(val, reg);
+}
+
+
+static u32 hal2_i_read32(struct snd_hal2 *hal2, u16 addr)
+{
+ u32 ret;
+ struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+ hal2_write(H2_READ_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+ ret = hal2_read(&regs->idr0) & 0xffff;
+ hal2_write(H2_READ_ADDR(addr) | 0x1, &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+ ret |= (hal2_read(&regs->idr0) & 0xffff) << 16;
+ return ret;
+}
+
+static void hal2_i_write16(struct snd_hal2 *hal2, u16 addr, u16 val)
+{
+ struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+ hal2_write(val, &regs->idr0);
+ hal2_write(0, &regs->idr1);
+ hal2_write(0, &regs->idr2);
+ hal2_write(0, &regs->idr3);
+ hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_write32(struct snd_hal2 *hal2, u16 addr, u32 val)
+{
+ struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+ hal2_write(val & 0xffff, &regs->idr0);
+ hal2_write(val >> 16, &regs->idr1);
+ hal2_write(0, &regs->idr2);
+ hal2_write(0, &regs->idr3);
+ hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_setbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
+{
+ struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+ hal2_write(H2_READ_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+ hal2_write((hal2_read(&regs->idr0) & 0xffff) | bit, &regs->idr0);
+ hal2_write(0, &regs->idr1);
+ hal2_write(0, &regs->idr2);
+ hal2_write(0, &regs->idr3);
+ hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_clearbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
+{
+ struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+ hal2_write(H2_READ_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+ hal2_write((hal2_read(&regs->idr0) & 0xffff) & ~bit, &regs->idr0);
+ hal2_write(0, &regs->idr1);
+ hal2_write(0, &regs->idr2);
+ hal2_write(0, &regs->idr3);
+ hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+ H2_INDIRECT_WAIT(regs);
+}
+
+static int hal2_gain_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;
+ switch ((int)kcontrol->private_value) {
+ case H2_MIX_OUTPUT_ATT:
+ uinfo->value.integer.max = 31;
+ break;
+ case H2_MIX_INPUT_GAIN:
+ uinfo->value.integer.max = 15;
+ break;
+ }
+ return 0;
+}
+
+static int hal2_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
+ u32 tmp;
+ int l, r;
+
+ switch ((int)kcontrol->private_value) {
+ case H2_MIX_OUTPUT_ATT:
+ tmp = hal2_i_read32(hal2, H2I_DAC_C2);
+ if (tmp & H2I_C2_MUTE) {
+ l = 0;
+ r = 0;
+ } else {
+ l = 31 - ((tmp >> H2I_C2_L_ATT_SHIFT) & 31);
+ r = 31 - ((tmp >> H2I_C2_R_ATT_SHIFT) & 31);
+ }
+ break;
+ case H2_MIX_INPUT_GAIN:
+ tmp = hal2_i_read32(hal2, H2I_ADC_C2);
+ l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
+ r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
+ break;
+ }
+ ucontrol->value.integer.value[0] = l;
+ ucontrol->value.integer.value[1] = r;
+
+ return 0;
+}
+
+static int hal2_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
+ u32 old, new;
+ int l, r;
+
+ l = ucontrol->value.integer.value[0];
+ r = ucontrol->value.integer.value[1];
+
+ switch ((int)kcontrol->private_value) {
+ case H2_MIX_OUTPUT_ATT:
+ old = hal2_i_read32(hal2, H2I_DAC_C2);
+ new = old & ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
+ if (l | r) {
+ l = 31 - l;
+ r = 31 - r;
+ new |= (l << H2I_C2_L_ATT_SHIFT);
+ new |= (r << H2I_C2_R_ATT_SHIFT);
+ } else
+ new |= H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE;
+ hal2_i_write32(hal2, H2I_DAC_C2, new);
+ break;
+ case H2_MIX_INPUT_GAIN:
+ old = hal2_i_read32(hal2, H2I_ADC_C2);
+ new = old & ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
+ new |= (l << H2I_C2_L_GAIN_SHIFT);
+ new |= (r << H2I_C2_R_GAIN_SHIFT);
+ hal2_i_write32(hal2, H2I_ADC_C2, new);
+ break;
+ }
+ return old != new;
+}
+
+static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = H2_MIX_OUTPUT_ATT,
+ .info = hal2_gain_info,
+ .get = hal2_gain_get,
+ .put = hal2_gain_put,
+};
+
+static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = H2_MIX_INPUT_GAIN,
+ .info = hal2_gain_info,
+ .get = hal2_gain_get,
+ .put = hal2_gain_put,
+};
+
+static int __devinit hal2_mixer_create(struct snd_hal2 *hal2)
+{
+ int err;
+
+ /* mute DAC */
+ hal2_i_write32(hal2, H2I_DAC_C2,
+ H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
+ /* mute ADC */
+ hal2_i_write32(hal2, H2I_ADC_C2, 0);
+
+ err = snd_ctl_add(hal2->card,
+ snd_ctl_new1(&hal2_ctrl_headphone, hal2));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(hal2->card,
+ snd_ctl_new1(&hal2_ctrl_mic, hal2));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static irqreturn_t hal2_interrupt(int irq, void *dev_id)
+{
+ struct snd_hal2 *hal2 = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+
+ /* decide what caused this interrupt */
+ if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
+ snd_pcm_period_elapsed(hal2->dac.substream);
+ ret = IRQ_HANDLED;
+ }
+ if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
+ snd_pcm_period_elapsed(hal2->adc.substream);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+
+static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
+{
+ unsigned short mod;
+
+ if (44100 % rate < 48000 % rate) {
+ mod = 4 * 44100 / rate;
+ codec->master = 44100;
+ } else {
+ mod = 4 * 48000 / rate;
+ codec->master = 48000;
+ }
+
+ codec->inc = 4;
+ codec->mod = mod;
+ rate = 4 * codec->master / mod;
+
+ return rate;
+}
+
+static void hal2_set_dac_rate(struct snd_hal2 *hal2)
+{
+ unsigned int master = hal2->dac.master;
+ int inc = hal2->dac.inc;
+ int mod = hal2->dac.mod;
+
+ hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
+ hal2_i_write32(hal2, H2I_BRES1_C2,
+ ((0xffff & (inc - mod - 1)) << 16) | inc);
+}
+
+static void hal2_set_adc_rate(struct snd_hal2 *hal2)
+{
+ unsigned int master = hal2->adc.master;
+ int inc = hal2->adc.inc;
+ int mod = hal2->adc.mod;
+
+ hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0);
+ hal2_i_write32(hal2, H2I_BRES2_C2,
+ ((0xffff & (inc - mod - 1)) << 16) | inc);
+}
+
+static void hal2_setup_dac(struct snd_hal2 *hal2)
+{
+ unsigned int fifobeg, fifoend, highwater, sample_size;
+ struct hal2_pbus *pbus = &hal2->dac.pbus;
+
+ /* Now we set up some PBUS information. The PBUS needs information about
+ * what portion of the fifo it will use. If it's receiving or
+ * transmitting, and finally whether the stream is little endian or big
+ * endian. The information is written later, on the start call.
+ */
+ sample_size = 2 * hal2->dac.voices;
+ /* Fifo should be set to hold exactly four samples. Highwater mark
+ * should be set to two samples. */
+ highwater = (sample_size * 2) >> 1; /* halfwords */
+ fifobeg = 0; /* playback is first */
+ fifoend = (sample_size * 4) >> 3; /* doublewords */
+ pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD |
+ (highwater << 8) | (fifobeg << 16) | (fifoend << 24);
+ /* We disable everything before we do anything at all */
+ pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+ hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
+ /* Setup the HAL2 for playback */
+ hal2_set_dac_rate(hal2);
+ /* Set endianess */
+ hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
+ /* Set DMA bus */
+ hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
+ /* We are using 1st Bresenham clock generator for playback */
+ hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
+ | (1 << H2I_C1_CLKID_SHIFT)
+ | (hal2->dac.voices << H2I_C1_DATAT_SHIFT));
+}
+
+static void hal2_setup_adc(struct snd_hal2 *hal2)
+{
+ unsigned int fifobeg, fifoend, highwater, sample_size;
+ struct hal2_pbus *pbus = &hal2->adc.pbus;
+
+ sample_size = 2 * hal2->adc.voices;
+ highwater = (sample_size * 2) >> 1; /* halfwords */
+ fifobeg = (4 * 4) >> 3; /* record is second */
+ fifoend = (4 * 4 + sample_size * 4) >> 3; /* doublewords */
+ pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD |
+ (highwater << 8) | (fifobeg << 16) | (fifoend << 24);
+ pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+ hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
+ /* Setup the HAL2 for record */
+ hal2_set_adc_rate(hal2);
+ /* Set endianess */
+ hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
+ /* Set DMA bus */
+ hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
+ /* We are using 2nd Bresenham clock generator for record */
+ hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
+ | (2 << H2I_C1_CLKID_SHIFT)
+ | (hal2->adc.voices << H2I_C1_DATAT_SHIFT));
+}
+
+static void hal2_start_dac(struct snd_hal2 *hal2)
+{
+ struct hal2_pbus *pbus = &hal2->dac.pbus;
+
+ pbus->pbus->pbdma_dptr = hal2->dac.desc_dma;
+ pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
+ /* enable DAC */
+ hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
+}
+
+static void hal2_start_adc(struct snd_hal2 *hal2)
+{
+ struct hal2_pbus *pbus = &hal2->adc.pbus;
+
+ pbus->pbus->pbdma_dptr = hal2->adc.desc_dma;
+ pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
+ /* enable ADC */
+ hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
+}
+
+static inline void hal2_stop_dac(struct snd_hal2 *hal2)
+{
+ hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+ /* The HAL2 itself may remain enabled safely */
+}
+
+static inline void hal2_stop_adc(struct snd_hal2 *hal2)
+{
+ hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+}
+
+static int hal2_alloc_dmabuf(struct hal2_codec *codec)
+{
+ struct hal2_desc *desc;
+ dma_addr_t desc_dma, buffer_dma;
+ int count = H2_BUF_SIZE / H2_BLOCK_SIZE;
+ int i;
+
+ codec->buffer = dma_alloc_noncoherent(NULL, H2_BUF_SIZE,
+ &buffer_dma, GFP_KERNEL);
+ if (!codec->buffer)
+ return -ENOMEM;
+ desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc),
+ &desc_dma, GFP_KERNEL);
+ if (!desc) {
+ dma_free_noncoherent(NULL, H2_BUF_SIZE,
+ codec->buffer, buffer_dma);
+ return -ENOMEM;
+ }
+ codec->buffer_dma = buffer_dma;
+ codec->desc_dma = desc_dma;
+ codec->desc = desc;
+ for (i = 0; i < count; i++) {
+ desc->desc.pbuf = buffer_dma + i * H2_BLOCK_SIZE;
+ desc->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
+ desc->desc.pnext = (i == count - 1) ?
+ desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc);
+ desc++;
+ }
+ dma_cache_sync(NULL, codec->desc, count * sizeof(struct hal2_desc),
+ DMA_TO_DEVICE);
+ codec->desc_count = count;
+ return 0;
+}
+
+static void hal2_free_dmabuf(struct hal2_codec *codec)
+{
+ dma_free_noncoherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
+ codec->desc, codec->desc_dma);
+ dma_free_noncoherent(NULL, H2_BUF_SIZE, codec->buffer,
+ codec->buffer_dma);
+}
+
+static struct snd_pcm_hardware hal2_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 65536,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+static int hal2_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int err;
+
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int hal2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int hal2_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ int err;
+
+ runtime->hw = hal2_pcm_hw;
+
+ err = hal2_alloc_dmabuf(&hal2->dac);
+ if (err)
+ return err;
+ return 0;
+}
+
+static int hal2_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+ hal2_free_dmabuf(&hal2->dac);
+ return 0;
+}
+
+static int hal2_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hal2_codec *dac = &hal2->dac;
+
+ dac->voices = runtime->channels;
+ dac->sample_rate = hal2_compute_rate(dac, runtime->rate);
+ memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect));
+ dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+ dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ dac->substream = substream;
+ hal2_setup_dac(hal2);
+ return 0;
+}
+
+static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ hal2->dac.pcm_indirect.hw_io = hal2->dac.buffer_dma;
+ hal2->dac.pcm_indirect.hw_data = 0;
+ substream->ops->ack(substream);
+ hal2_start_dac(hal2);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ hal2_stop_dac(hal2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t
+hal2_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct hal2_codec *dac = &hal2->dac;
+
+ return snd_pcm_indirect_playback_pointer(substream, &dac->pcm_indirect,
+ dac->pbus.pbus->pbdma_bptr);
+}
+
+static void hal2_playback_transfer(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect *rec, size_t bytes)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ unsigned char *buf = hal2->dac.buffer + rec->hw_data;
+
+ memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes);
+ dma_cache_sync(NULL, buf, bytes, DMA_TO_DEVICE);
+
+}
+
+static int hal2_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct hal2_codec *dac = &hal2->dac;
+
+ dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+ snd_pcm_indirect_playback_transfer(substream,
+ &dac->pcm_indirect,
+ hal2_playback_transfer);
+ return 0;
+}
+
+static int hal2_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct hal2_codec *adc = &hal2->adc;
+ int err;
+
+ runtime->hw = hal2_pcm_hw;
+
+ err = hal2_alloc_dmabuf(adc);
+ if (err)
+ return err;
+ return 0;
+}
+
+static int hal2_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+ hal2_free_dmabuf(&hal2->adc);
+ return 0;
+}
+
+static int hal2_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hal2_codec *adc = &hal2->adc;
+
+ adc->voices = runtime->channels;
+ adc->sample_rate = hal2_compute_rate(adc, runtime->rate);
+ memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect));
+ adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+ adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+ adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ adc->substream = substream;
+ hal2_setup_adc(hal2);
+ return 0;
+}
+
+static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ hal2->adc.pcm_indirect.hw_io = hal2->adc.buffer_dma;
+ hal2->adc.pcm_indirect.hw_data = 0;
+ printk(KERN_DEBUG "buffer_dma %x\n", hal2->adc.buffer_dma);
+ hal2_start_adc(hal2);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ hal2_stop_adc(hal2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t
+hal2_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct hal2_codec *adc = &hal2->adc;
+
+ return snd_pcm_indirect_capture_pointer(substream, &adc->pcm_indirect,
+ adc->pbus.pbus->pbdma_bptr);
+}
+
+static void hal2_capture_transfer(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect *rec, size_t bytes)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ unsigned char *buf = hal2->adc.buffer + rec->hw_data;
+
+ dma_cache_sync(NULL, buf, bytes, DMA_FROM_DEVICE);
+ memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
+}
+
+static int hal2_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+ struct hal2_codec *adc = &hal2->adc;
+
+ snd_pcm_indirect_capture_transfer(substream,
+ &adc->pcm_indirect,
+ hal2_capture_transfer);
+ return 0;
+}
+
+static struct snd_pcm_ops hal2_playback_ops = {
+ .open = hal2_playback_open,
+ .close = hal2_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = hal2_pcm_hw_params,
+ .hw_free = hal2_pcm_hw_free,
+ .prepare = hal2_playback_prepare,
+ .trigger = hal2_playback_trigger,
+ .pointer = hal2_playback_pointer,
+ .ack = hal2_playback_ack,
+};
+
+static struct snd_pcm_ops hal2_capture_ops = {
+ .open = hal2_capture_open,
+ .close = hal2_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = hal2_pcm_hw_params,
+ .hw_free = hal2_pcm_hw_free,
+ .prepare = hal2_capture_prepare,
+ .trigger = hal2_capture_trigger,
+ .pointer = hal2_capture_pointer,
+ .ack = hal2_capture_ack,
+};
+
+static int __devinit hal2_pcm_create(struct snd_hal2 *hal2)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ /* create first pcm device with one outputs and one input */
+ err = snd_pcm_new(hal2->card, "SGI HAL2 Audio", 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = hal2;
+ strcpy(pcm->name, "SGI HAL2");
+
+ /* set operators */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &hal2_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &hal2_capture_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 0, 1024 * 1024);
+
+ return 0;
+}
+
+static int hal2_dev_free(struct snd_device *device)
+{
+ struct snd_hal2 *hal2 = device->device_data;
+
+ free_irq(SGI_HPCDMA_IRQ, hal2);
+ kfree(hal2);
+ return 0;
+}
+
+static struct snd_device_ops hal2_ops = {
+ .dev_free = hal2_dev_free,
+};
+
+static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3,
+ int index)
+{
+ codec->pbus.pbusnr = index;
+ codec->pbus.pbus = &hpc3->pbdma[index];
+}
+
+static int hal2_detect(struct snd_hal2 *hal2)
+{
+ unsigned short board, major, minor;
+ unsigned short rev;
+
+ /* reset HAL2 */
+ hal2_write(0, &hal2->ctl_regs->isr);
+
+ /* release reset */
+ hal2_write(H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N,
+ &hal2->ctl_regs->isr);
+
+
+ hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);
+ rev = hal2_read(&hal2->ctl_regs->rev);
+ if (rev & H2_REV_AUDIO_PRESENT)
+ return -ENODEV;
+
+ board = (rev & H2_REV_BOARD_M) >> 12;
+ major = (rev & H2_REV_MAJOR_CHIP_M) >> 4;
+ minor = (rev & H2_REV_MINOR_CHIP_M);
+
+ printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n",
+ board, major, minor);
+
+ return 0;
+}
+
+static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
+{
+ struct snd_hal2 *hal2;
+ struct hpc3_regs *hpc3 = hpc3c0;
+ int err;
+
+ hal2 = kzalloc(sizeof(struct snd_hal2), GFP_KERNEL);
+ if (!hal2)
+ return -ENOMEM;
+
+ hal2->card = card;
+
+ if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
+ "SGI HAL2", hal2)) {
+ printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
+ kfree(hal2);
+ return -EAGAIN;
+ }
+
+ hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0];
+ hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1];
+ hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2];
+ hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3];
+
+ if (hal2_detect(hal2) < 0) {
+ kfree(hal2);
+ return -ENODEV;
+ }
+
+ hal2_init_codec(&hal2->dac, hpc3, 0);
+ hal2_init_codec(&hal2->adc, hpc3, 1);
+
+ /*
+ * All DMA channel interfaces in HAL2 are designed to operate with
+ * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles
+ * in D5. HAL2 is a 16-bit device which can accept both big and little
+ * endian format. It assumes that even address bytes are on high
+ * portion of PBUS (15:8) and assumes that HPC3 is programmed to
+ * accept a live (unsynchronized) version of P_DREQ_N from HAL2.
+ */
+#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \
+ (2 << HPC3_DMACFG_D4R_SHIFT) | \
+ (2 << HPC3_DMACFG_D5R_SHIFT) | \
+ (0 << HPC3_DMACFG_D3W_SHIFT) | \
+ (2 << HPC3_DMACFG_D4W_SHIFT) | \
+ (2 << HPC3_DMACFG_D5W_SHIFT) | \
+ HPC3_DMACFG_DS16 | \
+ HPC3_DMACFG_EVENHI | \
+ HPC3_DMACFG_RTIME | \
+ (8 << HPC3_DMACFG_BURST_SHIFT) | \
+ HPC3_DMACFG_DRQLIVE)
+ /*
+ * Ignore what's mentioned in the specification and write value which
+ * works in The Real World (TM)
+ */
+ hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
+ hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hal2, &hal2_ops);
+ if (err < 0) {
+ free_irq(SGI_HPCDMA_IRQ, hal2);
+ kfree(hal2);
+ return err;
+ }
+ *rchip = hal2;
+ return 0;
+}
+
+static int __devinit hal2_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct snd_hal2 *chip;
+ int err;
+
+ card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ err = hal2_create(card, &chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_card_set_dev(card, &pdev->dev);
+
+ err = hal2_pcm_create(chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ err = hal2_mixer_create(chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "SGI HAL2 Audio");
+ strcpy(card->shortname, "SGI HAL2 Audio");
+ sprintf(card->longname, "%s irq %i",
+ card->shortname,
+ SGI_HPCDMA_IRQ);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ platform_set_drvdata(pdev, card);
+ return 0;
+}
+
+static int __exit hal2_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ snd_card_free(card);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver hal2_driver = {
+ .probe = hal2_probe,
+ .remove = __devexit_p(hal2_remove),
+ .driver = {
+ .name = "sgihal2",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init alsa_card_hal2_init(void)
+{
+ return platform_driver_register(&hal2_driver);
+}
+
+static void __exit alsa_card_hal2_exit(void)
+{
+ platform_driver_unregister(&hal2_driver);
+}
+
+module_init(alsa_card_hal2_init);
+module_exit(alsa_card_hal2_exit);
diff --git a/sound/oss/hal2.h b/sound/mips/hal2.h
index 2bd3b52d8a3..f19828bc64e 100644
--- a/sound/oss/hal2.h
+++ b/sound/mips/hal2.h
@@ -7,7 +7,7 @@
* Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
*
* 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
+ * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
@@ -21,9 +21,6 @@
*
*/
-#include <asm/addrspace.h>
-#include <asm/sgi/hpc3.h>
-#include <linux/spinlock.h>
#include <linux/types.h>
/* Indirect status register */
@@ -156,8 +153,8 @@
/* Bits in CTL2 register */
-#define H2I_C2_R_GAIN_SHIFT 0 /* right a/d input gain */
-#define H2I_C2_R_GAIN_M 0xf
+#define H2I_C2_R_GAIN_SHIFT 0 /* right a/d input gain */
+#define H2I_C2_R_GAIN_M 0xf
#define H2I_C2_L_GAIN_SHIFT 4 /* left a/d input gain */
#define H2I_C2_L_GAIN_M 0xf0
#define H2I_C2_R_SEL 0x100 /* right input select */
@@ -180,7 +177,7 @@
#define H2I_BRES_C1_SHIFT 0 /* 0=48.0 1=44.1 2=aes_rx */
#define H2I_BRES_C1_M 0x03
-
+
/* Clock generator CTL 2, 32 bit */
#define H2I_BRES1_C2 0x2108
@@ -203,46 +200,46 @@
struct hal2_ctl_regs {
u32 _unused0[4];
- volatile u32 isr; /* 0x10 Status Register */
+ u32 isr; /* 0x10 Status Register */
u32 _unused1[3];
- volatile u32 rev; /* 0x20 Revision Register */
+ u32 rev; /* 0x20 Revision Register */
u32 _unused2[3];
- volatile u32 iar; /* 0x30 Indirect Address Register */
+ u32 iar; /* 0x30 Indirect Address Register */
u32 _unused3[3];
- volatile u32 idr0; /* 0x40 Indirect Data Register 0 */
+ u32 idr0; /* 0x40 Indirect Data Register 0 */
u32 _unused4[3];
- volatile u32 idr1; /* 0x50 Indirect Data Register 1 */
+ u32 idr1; /* 0x50 Indirect Data Register 1 */
u32 _unused5[3];
- volatile u32 idr2; /* 0x60 Indirect Data Register 2 */
+ u32 idr2; /* 0x60 Indirect Data Register 2 */
u32 _unused6[3];
- volatile u32 idr3; /* 0x70 Indirect Data Register 3 */
+ u32 idr3; /* 0x70 Indirect Data Register 3 */
};
struct hal2_aes_regs {
- volatile u32 rx_stat[2]; /* Status registers */
- volatile u32 rx_cr[2]; /* Control registers */
- volatile u32 rx_ud[4]; /* User data window */
- volatile u32 rx_st[24]; /* Channel status data */
-
- volatile u32 tx_stat[1]; /* Status register */
- volatile u32 tx_cr[3]; /* Control registers */
- volatile u32 tx_ud[4]; /* User data window */
- volatile u32 tx_st[24]; /* Channel status data */
+ u32 rx_stat[2]; /* Status registers */
+ u32 rx_cr[2]; /* Control registers */
+ u32 rx_ud[4]; /* User data window */
+ u32 rx_st[24]; /* Channel status data */
+
+ u32 tx_stat[1]; /* Status register */
+ u32 tx_cr[3]; /* Control registers */
+ u32 tx_ud[4]; /* User data window */
+ u32 tx_st[24]; /* Channel status data */
};
struct hal2_vol_regs {
- volatile u32 right; /* Right volume */
- volatile u32 left; /* Left volume */
+ u32 right; /* Right volume */
+ u32 left; /* Left volume */
};
struct hal2_syn_regs {
u32 _unused0[2];
- volatile u32 page; /* DOC Page register */
- volatile u32 regsel; /* DOC Register selection */
- volatile u32 dlow; /* DOC Data low */
- volatile u32 dhigh; /* DOC Data high */
- volatile u32 irq; /* IRQ Status */
- volatile u32 dram; /* DRAM Access */
+ u32 page; /* DOC Page register */
+ u32 regsel; /* DOC Register selection */
+ u32 dlow; /* DOC Data low */
+ u32 dhigh; /* DOC Data high */
+ u32 irq; /* IRQ Status */
+ u32 dram; /* DRAM Access */
};
#endif /* __HAL2_H */
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
new file mode 100644
index 00000000000..4c63504348d
--- /dev/null
+++ b/sound/mips/sgio2audio.c
@@ -0,0 +1,1006 @@
+/*
+ * Sound driver for Silicon Graphics O2 Workstations A/V board audio.
+ *
+ * Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ * Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ * Mxier part taken from mace_audio.c:
+ * Copyright 2007 Thorben Jändling <tj.trevelyan@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/ip32/ip32_ints.h>
+#include <asm/ip32/mace.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/ad1843.h>
+
+
+MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>");
+MODULE_DESCRIPTION("SGI O2 Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}");
+
+static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SGI O2 soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard.");
+
+
+#define AUDIO_CONTROL_RESET BIT(0) /* 1: reset audio interface */
+#define AUDIO_CONTROL_CODEC_PRESENT BIT(1) /* 1: codec detected */
+
+#define CODEC_CONTROL_WORD_SHIFT 0
+#define CODEC_CONTROL_READ BIT(16)
+#define CODEC_CONTROL_ADDRESS_SHIFT 17
+
+#define CHANNEL_CONTROL_RESET BIT(10) /* 1: reset channel */
+#define CHANNEL_DMA_ENABLE BIT(9) /* 1: enable DMA transfer */
+#define CHANNEL_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */
+#define CHANNEL_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */
+#define CHANNEL_INT_THRESHOLD_50 (2 << 5) /* int on buffer >50% full */
+#define CHANNEL_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */
+#define CHANNEL_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */
+#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
+#define CHANNEL_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */
+#define CHANNEL_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */
+
+#define CHANNEL_RING_SHIFT 12
+#define CHANNEL_RING_SIZE (1 << CHANNEL_RING_SHIFT)
+#define CHANNEL_RING_MASK (CHANNEL_RING_SIZE - 1)
+
+#define CHANNEL_LEFT_SHIFT 40
+#define CHANNEL_RIGHT_SHIFT 8
+
+struct snd_sgio2audio_chan {
+ int idx;
+ struct snd_pcm_substream *substream;
+ int pos;
+ snd_pcm_uframes_t size;
+ spinlock_t lock;
+};
+
+/* definition of the chip-specific record */
+struct snd_sgio2audio {
+ struct snd_card *card;
+
+ /* codec */
+ struct snd_ad1843 ad1843;
+ spinlock_t ad1843_lock;
+
+ /* channels */
+ struct snd_sgio2audio_chan channel[3];
+
+ /* resources */
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+};
+
+/* AD1843 access */
+
+/*
+ * read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
+ *
+ * Returns unsigned register value on success, -errno on failure.
+ */
+static int read_ad1843_reg(void *priv, int reg)
+{
+ struct snd_sgio2audio *chip = priv;
+ int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+ writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
+ CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
+ wmb();
+ val = readq(&mace->perif.audio.codec_control); /* flush bus */
+ udelay(200);
+
+ val = readq(&mace->perif.audio.codec_read);
+
+ spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+ return val;
+}
+
+/*
+ * write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
+ */
+static int write_ad1843_reg(void *priv, int reg, int word)
+{
+ struct snd_sgio2audio *chip = priv;
+ int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+ writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
+ (word << CODEC_CONTROL_WORD_SHIFT),
+ &mace->perif.audio.codec_control);
+ wmb();
+ val = readq(&mace->perif.audio.codec_control); /* flush bus */
+ udelay(200);
+
+ spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+ return 0;
+}
+
+static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843,
+ (int)kcontrol->private_value);
+ return 0;
+}
+
+static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+ int vol;
+
+ vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value);
+
+ ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF;
+ ucontrol->value.integer.value[1] = vol & 0xFF;
+
+ return 0;
+}
+
+static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+ int newvol, oldvol;
+
+ oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value);
+ newvol = (ucontrol->value.integer.value[0] << 8) |
+ ucontrol->value.integer.value[1];
+
+ newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value,
+ newvol);
+
+ return newvol != oldvol;
+}
+
+static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *texts[3] = {
+ "Cam Mic", "Mic", "Line"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item >= 3)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843);
+ return 0;
+}
+
+static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+ int newsrc, oldsrc;
+
+ oldsrc = ad1843_get_recsrc(&chip->ad1843);
+ newsrc = ad1843_set_recsrc(&chip->ad1843,
+ ucontrol->value.enumerated.item[0]);
+
+ return newsrc != oldsrc;
+}
+
+/* dac1/pcm0 mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = AD1843_GAIN_PCM_0,
+ .info = sgio2audio_gain_info,
+ .get = sgio2audio_gain_get,
+ .put = sgio2audio_gain_put,
+};
+
+/* dac2/pcm1 mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .index = 1,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = AD1843_GAIN_PCM_1,
+ .info = sgio2audio_gain_info,
+ .get = sgio2audio_gain_get,
+ .put = sgio2audio_gain_put,
+};
+
+/* record level mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = AD1843_GAIN_RECLEV,
+ .info = sgio2audio_gain_info,
+ .get = sgio2audio_gain_get,
+ .put = sgio2audio_gain_put,
+};
+
+/* record level source control */
+static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = sgio2audio_source_info,
+ .get = sgio2audio_source_get,
+ .put = sgio2audio_source_put,
+};
+
+/* line mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Volume",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = AD1843_GAIN_LINE,
+ .info = sgio2audio_gain_info,
+ .get = sgio2audio_gain_get,
+ .put = sgio2audio_gain_put,
+};
+
+/* cd mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Volume",
+ .index = 1,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = AD1843_GAIN_LINE_2,
+ .info = sgio2audio_gain_info,
+ .get = sgio2audio_gain_get,
+ .put = sgio2audio_gain_put,
+};
+
+/* mic mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = AD1843_GAIN_MIC,
+ .info = sgio2audio_gain_info,
+ .get = sgio2audio_gain_get,
+ .put = sgio2audio_gain_put,
+};
+
+
+static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
+{
+ int err;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_recsource, chip));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_line, chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_cd, chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&sgio2audio_ctrl_mic, chip));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* low-level audio interface DMA */
+
+/* get data out of bounce buffer, count must be a multiple of 32 */
+/* returns 1 if a period has elapsed */
+static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
+ unsigned int ch, unsigned int count)
+{
+ int ret;
+ unsigned long src_base, src_pos, dst_mask;
+ unsigned char *dst_base;
+ int dst_pos;
+ u64 *src;
+ s16 *dst;
+ u64 x;
+ unsigned long flags;
+ struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
+
+ spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+ src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
+ src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
+ dst_base = runtime->dma_area;
+ dst_pos = chip->channel[ch].pos;
+ dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+ /* check if a period has elapsed */
+ chip->channel[ch].size += (count >> 3); /* in frames */
+ ret = chip->channel[ch].size >= runtime->period_size;
+ chip->channel[ch].size %= runtime->period_size;
+
+ while (count) {
+ src = (u64 *)(src_base + src_pos);
+ dst = (s16 *)(dst_base + dst_pos);
+
+ x = *src;
+ dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff;
+ dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff;
+
+ src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK;
+ dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask;
+ count -= sizeof(u64);
+ }
+
+ writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
+ chip->channel[ch].pos = dst_pos;
+
+ spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+ return ret;
+}
+
+/* put some DMA data in bounce buffer, count must be a multiple of 32 */
+/* returns 1 if a period has elapsed */
+static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
+ unsigned int ch, unsigned int count)
+{
+ int ret;
+ s64 l, r;
+ unsigned long dst_base, dst_pos, src_mask;
+ unsigned char *src_base;
+ int src_pos;
+ u64 *dst;
+ s16 *src;
+ unsigned long flags;
+ struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
+
+ spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+ dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
+ dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
+ src_base = runtime->dma_area;
+ src_pos = chip->channel[ch].pos;
+ src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+ /* check if a period has elapsed */
+ chip->channel[ch].size += (count >> 3); /* in frames */
+ ret = chip->channel[ch].size >= runtime->period_size;
+ chip->channel[ch].size %= runtime->period_size;
+
+ while (count) {
+ src = (s16 *)(src_base + src_pos);
+ dst = (u64 *)(dst_base + dst_pos);
+
+ l = src[0]; /* sign extend */
+ r = src[1]; /* sign extend */
+
+ *dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) |
+ ((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT);
+
+ dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK;
+ src_pos = (src_pos + 2 * sizeof(s16)) & src_mask;
+ count -= sizeof(u64);
+ }
+
+ writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
+ chip->channel[ch].pos = src_pos;
+
+ spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+ return ret;
+}
+
+static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+ struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+ int ch = chan->idx;
+
+ /* reset DMA channel */
+ writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control);
+ udelay(10);
+ writeq(0, &mace->perif.audio.chan[ch].control);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* push a full buffer */
+ snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32);
+ }
+ /* set DMA to wake on 50% empty and enable interrupt */
+ writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50,
+ &mace->perif.audio.chan[ch].control);
+ return 0;
+}
+
+static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+
+ writeq(0, &mace->perif.audio.chan[chan->idx].control);
+ return 0;
+}
+
+static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id)
+{
+ struct snd_sgio2audio_chan *chan = dev_id;
+ struct snd_pcm_substream *substream;
+ struct snd_sgio2audio *chip;
+ int count, ch;
+
+ substream = chan->substream;
+ chip = snd_pcm_substream_chip(substream);
+ ch = chan->idx;
+
+ /* empty the ring */
+ count = CHANNEL_RING_SIZE -
+ readq(&mace->perif.audio.chan[ch].depth) - 32;
+ if (snd_sgio2audio_dma_pull_frag(chip, ch, count))
+ snd_pcm_period_elapsed(substream);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id)
+{
+ struct snd_sgio2audio_chan *chan = dev_id;
+ struct snd_pcm_substream *substream;
+ struct snd_sgio2audio *chip;
+ int count, ch;
+
+ substream = chan->substream;
+ chip = snd_pcm_substream_chip(substream);
+ ch = chan->idx;
+ /* fill the ring */
+ count = CHANNEL_RING_SIZE -
+ readq(&mace->perif.audio.chan[ch].depth) - 32;
+ if (snd_sgio2audio_dma_push_frag(chip, ch, count))
+ snd_pcm_period_elapsed(substream);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
+{
+ struct snd_sgio2audio_chan *chan = dev_id;
+ struct snd_pcm_substream *substream;
+
+ substream = chan->substream;
+ snd_sgio2audio_dma_stop(substream);
+ snd_sgio2audio_dma_start(substream);
+ return IRQ_HANDLED;
+}
+
+/* PCM part */
+/* PCM hardware definition */
+static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 32768,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+/* PCM playback open callback */
+static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_sgio2audio_pcm_hw;
+ runtime->private_data = &chip->channel[1];
+ return 0;
+}
+
+static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_sgio2audio_pcm_hw;
+ runtime->private_data = &chip->channel[2];
+ return 0;
+}
+
+/* PCM capture open callback */
+static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_sgio2audio_pcm_hw;
+ runtime->private_data = &chip->channel[0];
+ return 0;
+}
+
+/* PCM close callback */
+static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->private_data = NULL;
+ return 0;
+}
+
+
+/* hw_params callback */
+static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int size = params_buffer_bytes(hw_params);
+
+ /* alloc virtual 'dma' area */
+ if (runtime->dma_area)
+ vfree(runtime->dma_area);
+ runtime->dma_area = vmalloc(size);
+ if (runtime->dma_area == NULL)
+ return -ENOMEM;
+ runtime->dma_bytes = size;
+ return 0;
+}
+
+/* hw_free callback */
+static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ if (substream->runtime->dma_area)
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ return 0;
+}
+
+/* prepare callback */
+static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+ int ch = chan->idx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+ /* Setup the pseudo-dma transfer pointers. */
+ chip->channel[ch].pos = 0;
+ chip->channel[ch].size = 0;
+ chip->channel[ch].substream = substream;
+
+ /* set AD1843 format */
+ /* hardware format is always S16_LE */
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ad1843_setup_dac(&chip->ad1843,
+ ch - 1,
+ runtime->rate,
+ SNDRV_PCM_FORMAT_S16_LE,
+ runtime->channels);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ ad1843_setup_adc(&chip->ad1843,
+ runtime->rate,
+ SNDRV_PCM_FORMAT_S16_LE,
+ runtime->channels);
+ break;
+ }
+ spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+ return 0;
+}
+
+/* trigger callback */
+static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* start the PCM engine */
+ snd_sgio2audio_dma_start(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* stop the PCM engine */
+ snd_sgio2audio_dma_stop(substream);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+ struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+
+ /* get the current hardware pointer */
+ return bytes_to_frames(substream->runtime,
+ chip->channel[chan->idx].pos);
+}
+
+/* get the physical page pointer on the given offset */
+static struct page *snd_sgio2audio_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+
+/* operators */
+static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
+ .open = snd_sgio2audio_playback1_open,
+ .close = snd_sgio2audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sgio2audio_pcm_hw_params,
+ .hw_free = snd_sgio2audio_pcm_hw_free,
+ .prepare = snd_sgio2audio_pcm_prepare,
+ .trigger = snd_sgio2audio_pcm_trigger,
+ .pointer = snd_sgio2audio_pcm_pointer,
+ .page = snd_sgio2audio_page,
+};
+
+static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
+ .open = snd_sgio2audio_playback2_open,
+ .close = snd_sgio2audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sgio2audio_pcm_hw_params,
+ .hw_free = snd_sgio2audio_pcm_hw_free,
+ .prepare = snd_sgio2audio_pcm_prepare,
+ .trigger = snd_sgio2audio_pcm_trigger,
+ .pointer = snd_sgio2audio_pcm_pointer,
+ .page = snd_sgio2audio_page,
+};
+
+static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
+ .open = snd_sgio2audio_capture_open,
+ .close = snd_sgio2audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sgio2audio_pcm_hw_params,
+ .hw_free = snd_sgio2audio_pcm_hw_free,
+ .prepare = snd_sgio2audio_pcm_prepare,
+ .trigger = snd_sgio2audio_pcm_trigger,
+ .pointer = snd_sgio2audio_pcm_pointer,
+ .page = snd_sgio2audio_page,
+};
+
+/*
+ * definitions of capture are omitted here...
+ */
+
+/* create a pcm device */
+static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ /* create first pcm device with one outputs and one input */
+ err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = chip;
+ strcpy(pcm->name, "SGI O2 DAC1");
+
+ /* set operators */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_sgio2audio_playback1_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_sgio2audio_capture_ops);
+
+ /* create second pcm device with one outputs and no input */
+ err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = chip;
+ strcpy(pcm->name, "SGI O2 DAC2");
+
+ /* set operators */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_sgio2audio_playback2_ops);
+
+ return 0;
+}
+
+static struct {
+ int idx;
+ int irq;
+ irqreturn_t (*isr)(int, void *);
+ const char *desc;
+} snd_sgio2_isr_table[] = {
+ {
+ .idx = 0,
+ .irq = MACEISA_AUDIO1_DMAT_IRQ,
+ .isr = snd_sgio2audio_dma_in_isr,
+ .desc = "Capture DMA Channel 0"
+ }, {
+ .idx = 0,
+ .irq = MACEISA_AUDIO1_OF_IRQ,
+ .isr = snd_sgio2audio_error_isr,
+ .desc = "Capture Overflow"
+ }, {
+ .idx = 1,
+ .irq = MACEISA_AUDIO2_DMAT_IRQ,
+ .isr = snd_sgio2audio_dma_out_isr,
+ .desc = "Playback DMA Channel 1"
+ }, {
+ .idx = 1,
+ .irq = MACEISA_AUDIO2_MERR_IRQ,
+ .isr = snd_sgio2audio_error_isr,
+ .desc = "Memory Error Channel 1"
+ }, {
+ .idx = 2,
+ .irq = MACEISA_AUDIO3_DMAT_IRQ,
+ .isr = snd_sgio2audio_dma_out_isr,
+ .desc = "Playback DMA Channel 2"
+ }, {
+ .idx = 2,
+ .irq = MACEISA_AUDIO3_MERR_IRQ,
+ .isr = snd_sgio2audio_error_isr,
+ .desc = "Memory Error Channel 2"
+ }
+};
+
+/* ALSA driver */
+
+static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
+{
+ int i;
+
+ /* reset interface */
+ writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
+ udelay(1);
+ writeq(0, &mace->perif.audio.control);
+
+ /* release IRQ's */
+ for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++)
+ free_irq(snd_sgio2_isr_table[i].irq,
+ &chip->channel[snd_sgio2_isr_table[i].idx]);
+
+ dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+ chip->ring_base, chip->ring_base_dma);
+
+ /* release card data */
+ kfree(chip);
+ return 0;
+}
+
+static int snd_sgio2audio_dev_free(struct snd_device *device)
+{
+ struct snd_sgio2audio *chip = device->device_data;
+
+ return snd_sgio2audio_free(chip);
+}
+
+static struct snd_device_ops ops = {
+ .dev_free = snd_sgio2audio_dev_free,
+};
+
+static int __devinit snd_sgio2audio_create(struct snd_card *card,
+ struct snd_sgio2audio **rchip)
+{
+ struct snd_sgio2audio *chip;
+ int i, err;
+
+ *rchip = NULL;
+
+ /* check if a codec is attached to the interface */
+ /* (Audio or Audio/Video board present) */
+ if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
+ return -ENOENT;
+
+ chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->card = card;
+
+ chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+ &chip->ring_base_dma, GFP_USER);
+ if (chip->ring_base == NULL) {
+ printk(KERN_ERR
+ "sgio2audio: could not allocate ring buffers\n");
+ kfree(chip);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->ad1843_lock);
+
+ /* initialize channels */
+ for (i = 0; i < 3; i++) {
+ spin_lock_init(&chip->channel[i].lock);
+ chip->channel[i].idx = i;
+ }
+
+ /* allocate IRQs */
+ for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) {
+ if (request_irq(snd_sgio2_isr_table[i].irq,
+ snd_sgio2_isr_table[i].isr,
+ 0,
+ snd_sgio2_isr_table[i].desc,
+ &chip->channel[snd_sgio2_isr_table[i].idx])) {
+ snd_sgio2audio_free(chip);
+ printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n",
+ snd_sgio2_isr_table[i].irq);
+ return -EBUSY;
+ }
+ }
+
+ /* reset the interface */
+ writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
+ udelay(1);
+ writeq(0, &mace->perif.audio.control);
+ msleep_interruptible(1); /* give time to recover */
+
+ /* set ring base */
+ writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase);
+
+ /* attach the AD1843 codec */
+ chip->ad1843.read = read_ad1843_reg;
+ chip->ad1843.write = write_ad1843_reg;
+ chip->ad1843.chip = chip;
+
+ /* initialize the AD1843 codec */
+ err = ad1843_init(&chip->ad1843);
+ if (err < 0) {
+ snd_sgio2audio_free(chip);
+ return err;
+ }
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_sgio2audio_free(chip);
+ return err;
+ }
+ *rchip = chip;
+ return 0;
+}
+
+static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct snd_sgio2audio *chip;
+ int err;
+
+ card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ err = snd_sgio2audio_create(card, &chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_card_set_dev(card, &pdev->dev);
+
+ err = snd_sgio2audio_new_pcm(chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ err = snd_sgio2audio_new_mixer(chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "SGI O2 Audio");
+ strcpy(card->shortname, "SGI O2 Audio");
+ sprintf(card->longname, "%s irq %i-%i",
+ card->shortname,
+ MACEISA_AUDIO1_DMAT_IRQ,
+ MACEISA_AUDIO3_MERR_IRQ);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ platform_set_drvdata(pdev, card);
+ return 0;
+}
+
+static int __exit snd_sgio2audio_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ snd_card_free(card);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver sgio2audio_driver = {
+ .probe = snd_sgio2audio_probe,
+ .remove = __devexit_p(snd_sgio2audio_remove),
+ .driver = {
+ .name = "sgio2audio",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init alsa_card_sgio2audio_init(void)
+{
+ return platform_driver_register(&sgio2audio_driver);
+}
+
+static void __exit alsa_card_sgio2audio_exit(void)
+{
+ platform_driver_unregister(&sgio2audio_driver);
+}
+
+module_init(alsa_card_sgio2audio_init)
+module_exit(alsa_card_sgio2audio_exit)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 3be2dc1025b..1ca7427c4b6 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -7,7 +7,7 @@
config SOUND_BCM_CS4297A
tristate "Crystal Sound CS4297a (for Swarm)"
- depends on SOUND_PRIME && SIBYTE_SWARM
+ depends on SIBYTE_SWARM
help
The BCM91250A has a Crystal CS4297a on synchronous serial
port B (in addition to the DB-9 serial port). Say Y or M
@@ -17,69 +17,20 @@ config SOUND_BCM_CS4297A
config SOUND_VWSND
tristate "SGI Visual Workstation Sound"
- depends on SOUND_PRIME && X86_VISWS
+ depends on X86_VISWS
help
Say Y or M if you have an SGI Visual Workstation and you want to be
able to use its on-board audio. Read
<file:Documentation/sound/oss/vwsnd> for more info on this driver's
capabilities.
-config SOUND_HAL2
- tristate "SGI HAL2 sound (EXPERIMENTAL)"
- depends on SOUND_PRIME && SGI_IP22 && EXPERIMENTAL
- help
- Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to
- use its on-board A2 audio system.
-
config SOUND_AU1550_AC97
tristate "Au1550/Au1200 AC97 Sound"
- select SND_AC97_CODEC
- depends on SOUND_PRIME && (SOC_AU1550 || SOC_AU1200)
-
-config SOUND_TRIDENT
- tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core"
- depends on SOUND_PRIME && PCI
- ---help---
- Say Y or M if you have a PCI sound card utilizing the Trident
- 4DWave-DX/NX chipset or your mother board chipset has SiS 7018
- or ALi 5451 built-in. The SiS 7018 PCI Audio Core is embedded
- in SiS960 Super South Bridge and SiS540/630 Single Chipset.
- The ALi 5451 PCI Audio Core is embedded in ALi M1535, M1535D,
- M1535+ or M1535D+ South Bridge.
-
- Use lspci -n to find out if your sound card or chipset uses
- Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands
- for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. PCI ID
- 10B9:5451 stands for ALi5451.
-
- This driver supports S/PDIF in/out (record/playback) for ALi 5451
- embedded in ALi M1535+ and M1535D+. Note that they aren't all
- enabled by default; you can enable them by saying Y to "/proc file
- system support" and "Sysctl support", and after the /proc file
- system has been mounted, executing the command
-
- command what is enabled
-
- echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default).
-
- echo 1>/proc/ALi5451 use S/PDIF out to output pcm data.
-
- echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data.
- (AC3...).
-
- echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...).
- (Default).
-
- echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF
- in.
-
-
- This driver differs slightly from OSS/Free, so PLEASE READ the
- comments at the top of <file:sound/oss/trident.c>.
+ depends on SOC_AU1550 || SOC_AU1200
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
- depends on SOUND_PRIME && (m || !STANDALONE) && ISA
+ depends on (m || !STANDALONE) && ISA
help
Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
Monterey (not for the Pinnacle or Fiji).
@@ -143,7 +94,7 @@ config MSNDCLAS_IO
config SOUND_MSNDPIN
tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
- depends on SOUND_PRIME && (m || !STANDALONE) && ISA
+ depends on (m || !STANDALONE) && ISA
help
Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
See <file:Documentation/sound/oss/MultiSound> for important information
@@ -229,7 +180,7 @@ config MSNDPIN_NONPNP
configure the card's resources.
comment "MSND Pinnacle DSP section will be configured to above parameters."
- depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+ depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
config MSNDPIN_CFG
hex "MSND Pinnacle config port 250,260,270"
@@ -242,7 +193,7 @@ config MSNDPIN_CFG
Mode".
comment "Pinnacle-specific Device Configuration (0 disables)"
- depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+ depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
config MSNDPIN_MPU_IO
hex "MSND Pinnacle MPU I/O (e.g. 330)"
@@ -294,7 +245,7 @@ config MSNDPIN_JOYSTICK_IO
config MSND_FIFOSIZE
int "MSND buffer size (kB)"
- depends on SOUND_PRIME && (SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y)
+ depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y
default "128"
help
Configures the size of each audio buffer, in kilobytes, for
@@ -302,9 +253,9 @@ config MSND_FIFOSIZE
and Pinnacle). Larger values reduce the chance of data overruns at
the expense of overall latency. If unsure, use the default.
-config SOUND_OSS
+menuconfig SOUND_OSS
tristate "OSS sound modules"
- depends on SOUND_PRIME && ISA_DMA_API && VIRT_TO_BUS
+ depends on ISA_DMA_API && VIRT_TO_BUS
help
OSS is the Open Sound System suite of sound card drivers. They make
sound programming easier since they provide a common API. Say Y or
@@ -312,16 +263,16 @@ config SOUND_OSS
driver for your sound card above, then pick your driver from the
list below.
+if SOUND_OSS
+
config SOUND_TRACEINIT
bool "Verbose initialisation"
- depends on SOUND_OSS
help
Verbose soundcard initialization -- affects the format of autoprobe
and initialization messages at boot time.
config SOUND_DMAP
bool "Persistent DMA buffers"
- depends on SOUND_OSS
---help---
Linux can often have problems allocating DMA buffers for ISA sound
cards on machines with more than 16MB of RAM. This is because ISA
@@ -338,8 +289,6 @@ config SOUND_DMAP
config SOUND_SSCAPE
tristate "Ensoniq SoundScape support"
- depends on SOUND_OSS
- depends on VIRT_TO_BUS
help
Answer Y if you have a sound card based on the Ensoniq SoundScape
chipset. Such cards are being manufactured at least by Ensoniq, Spea
@@ -352,13 +301,11 @@ config SOUND_SSCAPE
config SOUND_VMIDI
tristate "Loopback MIDI device support"
- depends on SOUND_OSS
help
Support for MIDI loopback on port 1 or 2.
config SOUND_TRIX
tristate "MediaTrix AudioTrix Pro support"
- depends on SOUND_OSS
help
Answer Y if you have the AudioTriX Pro sound card manufactured
by MediaTrix.
@@ -382,7 +329,6 @@ config TRIX_BOOT_FILE
config SOUND_MSS
tristate "Microsoft Sound System support"
- depends on SOUND_OSS
---help---
Again think carefully before answering Y to this question. It's
safe to answer Y if you have the original Windows Sound System card
@@ -414,7 +360,6 @@ config SOUND_MSS
config SOUND_MPU401
tristate "MPU-401 support (NOT for SB16)"
- depends on SOUND_OSS
---help---
Be careful with this question. The MPU401 interface is supported by
all sound cards. However, some natively supported cards have their
@@ -430,7 +375,6 @@ config SOUND_MPU401
config SOUND_PAS
tristate "ProAudioSpectrum 16 support"
- depends on SOUND_OSS
---help---
Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
16 or Logitech SoundMan 16 sound card. Answer N if you have some
@@ -452,7 +396,6 @@ config PAS_JOYSTICK
config SOUND_PSS
tristate "PSS (AD1848, ADSP-2115, ESC614) support"
- depends on SOUND_OSS
help
Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
@@ -495,7 +438,6 @@ config PSS_BOOT_FILE
config SOUND_SB
tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
- depends on SOUND_OSS
---help---
Answer Y if you have an original Sound Blaster card made by Creative
Labs or a 100% hardware compatible clone (like the Thunderboard or
@@ -522,7 +464,6 @@ config SOUND_SB
config SOUND_YM3812
tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
- depends on SOUND_OSS
---help---
Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
Answering Y is usually a safe and recommended choice, however some
@@ -538,7 +479,6 @@ config SOUND_YM3812
config SOUND_UART6850
tristate "6850 UART support"
- depends on SOUND_OSS
help
This option enables support for MIDI interfaces based on the 6850
UART chip. This interface is rarely found on sound cards. It's safe
@@ -549,7 +489,6 @@ config SOUND_UART6850
config SOUND_AEDSP16
tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
- depends on SOUND_OSS
---help---
Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
driver supports Audio Excel DSP 16 but not the III nor PnP versions
@@ -600,44 +539,16 @@ config SC6600_CDROMBASE
Base I/O port address for the CD-ROM interface of the Audio Excel
DSP 16 card.
-choice
- prompt "Audio Excel DSP 16"
- optional
- depends on SOUND_AEDSP16
-
-config AEDSP16_MSS
- bool "MSS emulation"
- depends on SOUND_MSS
- help
- Answer Y if you want your audio card to emulate Microsoft Sound
- System. You should then say Y to "Microsoft Sound System support"
- and say N to "Audio Excel DSP 16 (SBPro emulation)".
-
-config AEDSP16_SBPRO
- bool "SBPro emulation"
- depends on SOUND_SB
- help
- Answer Y if you want your audio card to emulate Sound Blaster Pro.
- You should then say Y to "100% Sound Blaster compatibles
- (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS
- emulation)".
-
- If you compile the driver into the kernel, you have to add
- "aedsp16=<io>,<irq>,<dma>,<mssio>,<mpuio>,<mouirq>" to the kernel
- command line.
-
-endchoice
-
config SOUND_VIDC
tristate "VIDC 16-bit sound"
- depends on ARM && (ARCH_ACORN || ARCH_CLPS7500) && SOUND_OSS
+ depends on ARM && (ARCH_ACORN || ARCH_CLPS7500)
help
16-bit support for the VIDC onboard sound hardware found on Acorn
machines.
config SOUND_WAVEARTIST
tristate "Netwinder WaveArtist"
- depends on ARM && SOUND_OSS && ARCH_NETWINDER
+ depends on ARM && ARCH_NETWINDER
help
Say Y here to include support for the Rockwell WaveArtist sound
system. This driver is mainly for the NetWinder.
@@ -646,9 +557,11 @@ config SOUND_KAHLUA
tristate "XpressAudio Sound Blaster emulation"
depends on SOUND_SB
+endif # SOUND_OSS
+
config SOUND_SH_DAC_AUDIO
tristate "SuperH DAC audio support"
- depends on SOUND_PRIME && CPU_SH3
+ depends on CPU_SH3
config SOUND_SH_DAC_AUDIO_CHANNEL
int "DAC channel"
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index 1f86299fae4..e0ae4d4d6a5 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_SOUND_OSS) += sound.o
# Please leave it as is, cause the link order is significant !
obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
-obj-$(CONFIG_SOUND_HAL2) += hal2.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
@@ -29,11 +28,8 @@ obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
-obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
-obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o
-
obj-$(CONFIG_DMASOUND) += dmasound/
# Declare multi-part drivers.
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
index b63839e8f9b..456a1b4d783 100644
--- a/sound/oss/ac97_codec.c
+++ b/sound/oss/ac97_codec.c
@@ -30,7 +30,7 @@
**************************************************************************
*
* History
- * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
* Removed non existant WM9700
* Added support for WM9705, WM9708, WM9709, WM9710, WM9711
* WM9712 and WM9717
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
index 51e1fde62e8..a0274f3dac0 100644
--- a/sound/oss/aedsp16.c
+++ b/sound/oss/aedsp16.c
@@ -29,14 +29,6 @@
#include "sound_config.h"
/*
- * Sanity checks
- */
-
-#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS)
-#error You have to enable only one of the MSS and SBPRO emulations.
-#endif
-
-/*
READ THIS
diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig
index 3eb782720e5..f456574a964 100644
--- a/sound/oss/dmasound/Kconfig
+++ b/sound/oss/dmasound/Kconfig
@@ -42,3 +42,4 @@ config DMASOUND_Q40
config DMASOUND
tristate
+ select SOUND_OSS_CORE
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index a003c0ea930..95fc5c68175 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -211,10 +211,6 @@ static int state_unit = -1;
static int irq_installed;
#endif /* MODULE */
-/* software implemented recording volume! */
-uint software_input_volume = SW_INPUT_VOLUME_SCALE * SW_INPUT_VOLUME_DEFAULT;
-EXPORT_SYMBOL(software_input_volume);
-
/* control over who can modify resources shared between play/record */
static mode_t shared_resource_owner;
static int shared_resources_initialised;
@@ -1188,7 +1184,7 @@ static struct {
/* publish this function for use by low-level code, if required */
-char *get_afmt_string(int afmt)
+static char *get_afmt_string(int afmt)
{
switch(afmt) {
case AFMT_MU_LAW:
@@ -1551,4 +1547,3 @@ EXPORT_SYMBOL(dmasound_catchRadius);
EXPORT_SYMBOL(dmasound_ulaw2dma8);
EXPORT_SYMBOL(dmasound_alaw2dma8);
#endif
-EXPORT_SYMBOL(get_afmt_string) ;
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 202e8103dc4..06e9e88e4c0 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -710,7 +710,7 @@ static MACHINE machAmiga = {
/*** Config & Setup **********************************************************/
-int __init dmasound_paula_init(void)
+static int __init dmasound_paula_init(void)
{
int err;
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
index b3379dd7ca5..1855b14d90c 100644
--- a/sound/oss/dmasound/dmasound_q40.c
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -611,7 +611,7 @@ static MACHINE machQ40 = {
/*** Config & Setup **********************************************************/
-int __init dmasound_q40_init(void)
+static int __init dmasound_q40_init(void)
{
if (MACH_IS_Q40) {
dmasound.mach = machQ40;
diff --git a/sound/oss/hal2.c b/sound/oss/hal2.c
deleted file mode 100644
index a94b9df489d..00000000000
--- a/sound/oss/hal2.c
+++ /dev/null
@@ -1,1558 +0,0 @@
-/*
- * Driver for A2 audio system used in SGI machines
- * Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
- *
- * Based on Ulf Carlsson's code.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Supported devices:
- * /dev/dsp standard dsp device, (mostly) OSS compatible
- * /dev/mixer standard mixer device, (mostly) OSS compatible
- *
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/sound.h>
-#include <linux/soundcard.h>
-#include <linux/mutex.h>
-
-
-#include <asm/io.h>
-#include <asm/sgi/hpc3.h>
-#include <asm/sgi/ip22.h>
-
-#include "hal2.h"
-
-#if 0
-#define DEBUG(args...) printk(args)
-#else
-#define DEBUG(args...)
-#endif
-
-#if 0
-#define DEBUG_MIX(args...) printk(args)
-#else
-#define DEBUG_MIX(args...)
-#endif
-
-/*
- * Before touching these look how it works. It is a bit unusual I know,
- * but it helps to keep things simple. This driver is considered complete
- * and I won't add any new features although hardware has many cool
- * capabilities.
- * (Historical note: HAL2 driver was first written by Ulf Carlsson - ALSA
- * 0.3 running with 2.2.x kernel. Then ALSA changed completely and it
- * seemed easier to me to write OSS driver from scratch - this one. Now
- * when ALSA is official part of 2.6 kernel it's time to write ALSA driver
- * using (hopefully) final version of ALSA interface)
- */
-#define H2_BLOCK_SIZE 1024
-#define H2_ADC_BUFSIZE 8192
-#define H2_DAC_BUFSIZE 16834
-
-struct hal2_pbus {
- struct hpc3_pbus_dmacregs *pbus;
- int pbusnr;
- unsigned int ctrl; /* Current state of pbus->pbdma_ctrl */
-};
-
-struct hal2_desc {
- struct hpc_dma_desc desc;
- u32 cnt; /* don't touch, it is also padding */
-};
-
-struct hal2_codec {
- unsigned char *buffer;
- struct hal2_desc *desc;
- int desc_count;
- int tail, head; /* tail index, head index */
- struct hal2_pbus pbus;
- unsigned int format; /* Audio data format */
- int voices; /* mono/stereo */
- unsigned int sample_rate;
- unsigned int master; /* Master frequency */
- unsigned short mod; /* MOD value */
- unsigned short inc; /* INC value */
-
- wait_queue_head_t dma_wait;
- spinlock_t lock;
- struct mutex sem;
-
- int usecount; /* recording and playback are
- * independent */
-};
-
-#define H2_MIX_OUTPUT_ATT 0
-#define H2_MIX_INPUT_GAIN 1
-#define H2_MIXERS 2
-struct hal2_mixer {
- int modcnt;
- unsigned int master;
- unsigned int volume[H2_MIXERS];
-};
-
-struct hal2_card {
- int dev_dsp; /* audio device */
- int dev_mixer; /* mixer device */
- int dev_midi; /* midi device */
-
- struct hal2_ctl_regs *ctl_regs; /* HAL2 ctl registers */
- struct hal2_aes_regs *aes_regs; /* HAL2 aes registers */
- struct hal2_vol_regs *vol_regs; /* HAL2 vol registers */
- struct hal2_syn_regs *syn_regs; /* HAL2 syn registers */
-
- struct hal2_codec dac;
- struct hal2_codec adc;
- struct hal2_mixer mixer;
-};
-
-#define H2_INDIRECT_WAIT(regs) while (regs->isr & H2_ISR_TSTATUS);
-
-#define H2_READ_ADDR(addr) (addr | (1<<7))
-#define H2_WRITE_ADDR(addr) (addr)
-
-static char *hal2str = "HAL2";
-
-/*
- * I doubt anyone has a machine with two HAL2 cards. It's possible to
- * have two HPC's, so it is probably possible to have two HAL2 cards.
- * Try to deal with it, but note that it is not tested.
- */
-#define MAXCARDS 2
-static struct hal2_card* hal2_card[MAXCARDS];
-
-static const struct {
- unsigned char idx:4, avail:1;
-} mixtable[SOUND_MIXER_NRDEVICES] = {
- [SOUND_MIXER_PCM] = { H2_MIX_OUTPUT_ATT, 1 }, /* voice */
- [SOUND_MIXER_MIC] = { H2_MIX_INPUT_GAIN, 1 }, /* mic */
-};
-
-#define H2_SUPPORTED_FORMATS (AFMT_S16_LE | AFMT_S16_BE)
-
-static inline void hal2_isr_write(struct hal2_card *hal2, u16 val)
-{
- hal2->ctl_regs->isr = val;
-}
-
-static inline u16 hal2_isr_look(struct hal2_card *hal2)
-{
- return hal2->ctl_regs->isr;
-}
-
-static inline u16 hal2_rev_look(struct hal2_card *hal2)
-{
- return hal2->ctl_regs->rev;
-}
-
-#ifdef HAL2_DUMP_REGS
-static u16 hal2_i_look16(struct hal2_card *hal2, u16 addr)
-{
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->iar = H2_READ_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
- return regs->idr0;
-}
-#endif
-
-static u32 hal2_i_look32(struct hal2_card *hal2, u16 addr)
-{
- u32 ret;
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->iar = H2_READ_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
- ret = regs->idr0 & 0xffff;
- regs->iar = H2_READ_ADDR(addr | 0x1);
- H2_INDIRECT_WAIT(regs);
- ret |= (regs->idr0 & 0xffff) << 16;
- return ret;
-}
-
-static void hal2_i_write16(struct hal2_card *hal2, u16 addr, u16 val)
-{
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->idr0 = val;
- regs->idr1 = 0;
- regs->idr2 = 0;
- regs->idr3 = 0;
- regs->iar = H2_WRITE_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_write32(struct hal2_card *hal2, u16 addr, u32 val)
-{
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->idr0 = val & 0xffff;
- regs->idr1 = val >> 16;
- regs->idr2 = 0;
- regs->idr3 = 0;
- regs->iar = H2_WRITE_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_setbit16(struct hal2_card *hal2, u16 addr, u16 bit)
-{
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->iar = H2_READ_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
- regs->idr0 = (regs->idr0 & 0xffff) | bit;
- regs->idr1 = 0;
- regs->idr2 = 0;
- regs->idr3 = 0;
- regs->iar = H2_WRITE_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_setbit32(struct hal2_card *hal2, u16 addr, u32 bit)
-{
- u32 tmp;
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->iar = H2_READ_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
- tmp = (regs->idr0 & 0xffff) | (regs->idr1 << 16) | bit;
- regs->idr0 = tmp & 0xffff;
- regs->idr1 = tmp >> 16;
- regs->idr2 = 0;
- regs->idr3 = 0;
- regs->iar = H2_WRITE_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_clearbit16(struct hal2_card *hal2, u16 addr, u16 bit)
-{
- struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
- regs->iar = H2_READ_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
- regs->idr0 = (regs->idr0 & 0xffff) & ~bit;
- regs->idr1 = 0;
- regs->idr2 = 0;
- regs->idr3 = 0;
- regs->iar = H2_WRITE_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
-}
-
-#if 0
-static void hal2_i_clearbit32(struct hal2_card *hal2, u16 addr, u32 bit)
-{
- u32 tmp;
- hal2_ctl_regs_t *regs = hal2->ctl_regs;
-
- regs->iar = H2_READ_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
- tmp = ((regs->idr0 & 0xffff) | (regs->idr1 << 16)) & ~bit;
- regs->idr0 = tmp & 0xffff;
- regs->idr1 = tmp >> 16;
- regs->idr2 = 0;
- regs->idr3 = 0;
- regs->iar = H2_WRITE_ADDR(addr);
- H2_INDIRECT_WAIT(regs);
-}
-#endif
-
-#ifdef HAL2_DUMP_REGS
-static void hal2_dump_regs(struct hal2_card *hal2)
-{
- DEBUG("isr: %08hx ", hal2_isr_look(hal2));
- DEBUG("rev: %08hx\n", hal2_rev_look(hal2));
- DEBUG("relay: %04hx\n", hal2_i_look16(hal2, H2I_RELAY_C));
- DEBUG("port en: %04hx ", hal2_i_look16(hal2, H2I_DMA_PORT_EN));
- DEBUG("dma end: %04hx ", hal2_i_look16(hal2, H2I_DMA_END));
- DEBUG("dma drv: %04hx\n", hal2_i_look16(hal2, H2I_DMA_DRV));
- DEBUG("syn ctl: %04hx ", hal2_i_look16(hal2, H2I_SYNTH_C));
- DEBUG("aesrx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESRX_C));
- DEBUG("aestx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESTX_C));
- DEBUG("dac ctl1: %04hx ", hal2_i_look16(hal2, H2I_ADC_C1));
- DEBUG("dac ctl2: %08x ", hal2_i_look32(hal2, H2I_ADC_C2));
- DEBUG("adc ctl1: %04hx ", hal2_i_look16(hal2, H2I_DAC_C1));
- DEBUG("adc ctl2: %08x ", hal2_i_look32(hal2, H2I_DAC_C2));
- DEBUG("syn map: %04hx\n", hal2_i_look16(hal2, H2I_SYNTH_MAP_C));
- DEBUG("bres1 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES1_C1));
- DEBUG("bres1 ctl2: %04x ", hal2_i_look32(hal2, H2I_BRES1_C2));
- DEBUG("bres2 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES2_C1));
- DEBUG("bres2 ctl2: %04x ", hal2_i_look32(hal2, H2I_BRES2_C2));
- DEBUG("bres3 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES3_C1));
- DEBUG("bres3 ctl2: %04x\n", hal2_i_look32(hal2, H2I_BRES3_C2));
-}
-#endif
-
-static struct hal2_card* hal2_dsp_find_card(int minor)
-{
- int i;
-
- for (i = 0; i < MAXCARDS; i++)
- if (hal2_card[i] != NULL && hal2_card[i]->dev_dsp == minor)
- return hal2_card[i];
- return NULL;
-}
-
-static struct hal2_card* hal2_mixer_find_card(int minor)
-{
- int i;
-
- for (i = 0; i < MAXCARDS; i++)
- if (hal2_card[i] != NULL && hal2_card[i]->dev_mixer == minor)
- return hal2_card[i];
- return NULL;
-}
-
-static void hal2_inc_head(struct hal2_codec *codec)
-{
- codec->head++;
- if (codec->head == codec->desc_count)
- codec->head = 0;
-}
-
-static void hal2_inc_tail(struct hal2_codec *codec)
-{
- codec->tail++;
- if (codec->tail == codec->desc_count)
- codec->tail = 0;
-}
-
-static void hal2_dac_interrupt(struct hal2_codec *dac)
-{
- int running;
-
- spin_lock(&dac->lock);
- /* if tail buffer contains zero samples DMA stream was already
- * stopped */
- running = dac->desc[dac->tail].cnt;
- dac->desc[dac->tail].cnt = 0;
- dac->desc[dac->tail].desc.cntinfo = HPCDMA_XIE | HPCDMA_EOX;
- /* we just proccessed empty buffer, don't update tail pointer */
- if (running)
- hal2_inc_tail(dac);
- spin_unlock(&dac->lock);
-
- wake_up(&dac->dma_wait);
-}
-
-static void hal2_adc_interrupt(struct hal2_codec *adc)
-{
- int running;
-
- spin_lock(&adc->lock);
- /* if head buffer contains nonzero samples DMA stream was already
- * stopped */
- running = !adc->desc[adc->head].cnt;
- adc->desc[adc->head].cnt = H2_BLOCK_SIZE;
- adc->desc[adc->head].desc.cntinfo = HPCDMA_XIE | HPCDMA_EOR;
- /* we just proccessed empty buffer, don't update head pointer */
- if (running)
- hal2_inc_head(adc);
- spin_unlock(&adc->lock);
-
- wake_up(&adc->dma_wait);
-}
-
-static irqreturn_t hal2_interrupt(int irq, void *dev_id)
-{
- struct hal2_card *hal2 = dev_id;
- irqreturn_t ret = IRQ_NONE;
-
- /* decide what caused this interrupt */
- if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
- hal2_dac_interrupt(&hal2->dac);
- ret = IRQ_HANDLED;
- }
- if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
- hal2_adc_interrupt(&hal2->adc);
- ret = IRQ_HANDLED;
- }
- return ret;
-}
-
-static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
-{
- unsigned short mod;
-
- DEBUG("rate: %d\n", rate);
-
- if (rate < 4000) rate = 4000;
- else if (rate > 48000) rate = 48000;
-
- if (44100 % rate < 48000 % rate) {
- mod = 4 * 44100 / rate;
- codec->master = 44100;
- } else {
- mod = 4 * 48000 / rate;
- codec->master = 48000;
- }
-
- codec->inc = 4;
- codec->mod = mod;
- rate = 4 * codec->master / mod;
-
- DEBUG("real_rate: %d\n", rate);
-
- return rate;
-}
-
-static void hal2_set_dac_rate(struct hal2_card *hal2)
-{
- unsigned int master = hal2->dac.master;
- int inc = hal2->dac.inc;
- int mod = hal2->dac.mod;
-
- DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod);
-
- hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
- hal2_i_write32(hal2, H2I_BRES1_C2, ((0xffff & (inc - mod - 1)) << 16) | inc);
-}
-
-static void hal2_set_adc_rate(struct hal2_card *hal2)
-{
- unsigned int master = hal2->adc.master;
- int inc = hal2->adc.inc;
- int mod = hal2->adc.mod;
-
- DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod);
-
- hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0);
- hal2_i_write32(hal2, H2I_BRES2_C2, ((0xffff & (inc - mod - 1)) << 16) | inc);
-}
-
-static void hal2_setup_dac(struct hal2_card *hal2)
-{
- unsigned int fifobeg, fifoend, highwater, sample_size;
- struct hal2_pbus *pbus = &hal2->dac.pbus;
-
- DEBUG("hal2_setup_dac\n");
-
- /* Now we set up some PBUS information. The PBUS needs information about
- * what portion of the fifo it will use. If it's receiving or
- * transmitting, and finally whether the stream is little endian or big
- * endian. The information is written later, on the start call.
- */
- sample_size = 2 * hal2->dac.voices;
- /* Fifo should be set to hold exactly four samples. Highwater mark
- * should be set to two samples. */
- highwater = (sample_size * 2) >> 1; /* halfwords */
- fifobeg = 0; /* playback is first */
- fifoend = (sample_size * 4) >> 3; /* doublewords */
- pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD |
- (highwater << 8) | (fifobeg << 16) | (fifoend << 24) |
- (hal2->dac.format & AFMT_S16_LE ? HPC3_PDMACTRL_SEL : 0);
- /* We disable everything before we do anything at all */
- pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
- hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
- /* Setup the HAL2 for playback */
- hal2_set_dac_rate(hal2);
- /* Set endianess */
- if (hal2->dac.format & AFMT_S16_LE)
- hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
- else
- hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
- /* Set DMA bus */
- hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
- /* We are using 1st Bresenham clock generator for playback */
- hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
- | (1 << H2I_C1_CLKID_SHIFT)
- | (hal2->dac.voices << H2I_C1_DATAT_SHIFT));
-}
-
-static void hal2_setup_adc(struct hal2_card *hal2)
-{
- unsigned int fifobeg, fifoend, highwater, sample_size;
- struct hal2_pbus *pbus = &hal2->adc.pbus;
-
- DEBUG("hal2_setup_adc\n");
-
- sample_size = 2 * hal2->adc.voices;
- highwater = (sample_size * 2) >> 1; /* halfwords */
- fifobeg = (4 * 4) >> 3; /* record is second */
- fifoend = (4 * 4 + sample_size * 4) >> 3; /* doublewords */
- pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD |
- (highwater << 8) | (fifobeg << 16) | (fifoend << 24) |
- (hal2->adc.format & AFMT_S16_LE ? HPC3_PDMACTRL_SEL : 0);
- pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
- hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
- /* Setup the HAL2 for record */
- hal2_set_adc_rate(hal2);
- /* Set endianess */
- if (hal2->adc.format & AFMT_S16_LE)
- hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
- else
- hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
- /* Set DMA bus */
- hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
- /* We are using 2nd Bresenham clock generator for record */
- hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
- | (2 << H2I_C1_CLKID_SHIFT)
- | (hal2->adc.voices << H2I_C1_DATAT_SHIFT));
-}
-
-static dma_addr_t hal2_desc_addr(struct hal2_codec *codec, int i)
-{
- if (--i < 0)
- i = codec->desc_count - 1;
- return codec->desc[i].desc.pnext;
-}
-
-static void hal2_start_dac(struct hal2_card *hal2)
-{
- struct hal2_codec *dac = &hal2->dac;
- struct hal2_pbus *pbus = &dac->pbus;
-
- pbus->pbus->pbdma_dptr = hal2_desc_addr(dac, dac->tail);
- pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
- /* enable DAC */
- hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
-}
-
-static void hal2_start_adc(struct hal2_card *hal2)
-{
- struct hal2_codec *adc = &hal2->adc;
- struct hal2_pbus *pbus = &adc->pbus;
-
- pbus->pbus->pbdma_dptr = hal2_desc_addr(adc, adc->head);
- pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
- /* enable ADC */
- hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
-}
-
-static inline void hal2_stop_dac(struct hal2_card *hal2)
-{
- hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
- /* The HAL2 itself may remain enabled safely */
-}
-
-static inline void hal2_stop_adc(struct hal2_card *hal2)
-{
- hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
-}
-
-static int hal2_alloc_dmabuf(struct hal2_codec *codec, int size,
- int count, int cntinfo, int dir)
-{
- struct hal2_desc *desc, *dma_addr;
- int i;
-
- DEBUG("allocating %dk DMA buffer.\n", size / 1024);
-
- codec->buffer = (unsigned char *)__get_free_pages(GFP_KERNEL | GFP_DMA,
- get_order(size));
- if (!codec->buffer)
- return -ENOMEM;
- desc = dma_alloc_coherent(NULL, count * sizeof(struct hal2_desc),
- (dma_addr_t *)&dma_addr, GFP_KERNEL);
- if (!desc) {
- free_pages((unsigned long)codec->buffer, get_order(size));
- return -ENOMEM;
- }
- codec->desc = desc;
- for (i = 0; i < count; i++) {
- desc->desc.pbuf = dma_map_single(NULL,
- (void *)(codec->buffer + i * H2_BLOCK_SIZE),
- H2_BLOCK_SIZE, dir);
- desc->desc.cntinfo = cntinfo;
- desc->desc.pnext = (i == count - 1) ?
- (u32)dma_addr : (u32)(dma_addr + i + 1);
- desc->cnt = 0;
- desc++;
- }
- codec->desc_count = count;
- codec->head = codec->tail = 0;
- return 0;
-}
-
-static int hal2_alloc_dac_dmabuf(struct hal2_codec *codec)
-{
- return hal2_alloc_dmabuf(codec, H2_DAC_BUFSIZE,
- H2_DAC_BUFSIZE / H2_BLOCK_SIZE,
- HPCDMA_XIE | HPCDMA_EOX,
- DMA_TO_DEVICE);
-}
-
-static int hal2_alloc_adc_dmabuf(struct hal2_codec *codec)
-{
- return hal2_alloc_dmabuf(codec, H2_ADC_BUFSIZE,
- H2_ADC_BUFSIZE / H2_BLOCK_SIZE,
- HPCDMA_XIE | H2_BLOCK_SIZE,
- DMA_TO_DEVICE);
-}
-
-static void hal2_free_dmabuf(struct hal2_codec *codec, int size, int dir)
-{
- dma_addr_t dma_addr;
- int i;
-
- dma_addr = codec->desc[codec->desc_count - 1].desc.pnext;
- for (i = 0; i < codec->desc_count; i++)
- dma_unmap_single(NULL, codec->desc[i].desc.pbuf,
- H2_BLOCK_SIZE, dir);
- dma_free_coherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
- (void *)codec->desc, dma_addr);
- free_pages((unsigned long)codec->buffer, get_order(size));
-}
-
-static void hal2_free_dac_dmabuf(struct hal2_codec *codec)
-{
- return hal2_free_dmabuf(codec, H2_DAC_BUFSIZE, DMA_TO_DEVICE);
-}
-
-static void hal2_free_adc_dmabuf(struct hal2_codec *codec)
-{
- return hal2_free_dmabuf(codec, H2_ADC_BUFSIZE, DMA_FROM_DEVICE);
-}
-
-/*
- * Add 'count' bytes to 'buffer' from DMA ring buffers. Return number of
- * bytes added or -EFAULT if copy_from_user failed.
- */
-static int hal2_get_buffer(struct hal2_card *hal2, char *buffer, int count)
-{
- unsigned long flags;
- int size, ret = 0;
- unsigned char *buf;
- struct hal2_desc *tail;
- struct hal2_codec *adc = &hal2->adc;
-
- DEBUG("getting %d bytes ", count);
-
- spin_lock_irqsave(&adc->lock, flags);
- tail = &adc->desc[adc->tail];
- /* enable DMA stream if there are no data */
- if (!tail->cnt && !(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT))
- hal2_start_adc(hal2);
- while (tail->cnt > 0 && count > 0) {
- size = min((int)tail->cnt, count);
- buf = &adc->buffer[(adc->tail + 1) * H2_BLOCK_SIZE - tail->cnt];
- spin_unlock_irqrestore(&adc->lock, flags);
- dma_sync_single(NULL, tail->desc.pbuf, size, DMA_FROM_DEVICE);
- if (copy_to_user(buffer, buf, size)) {
- ret = -EFAULT;
- goto out;
- }
- spin_lock_irqsave(&adc->lock, flags);
- tail->cnt -= size;
- /* buffer is empty, update tail pointer */
- if (tail->cnt == 0) {
- tail->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
- hal2_inc_tail(adc);
- tail = &adc->desc[adc->tail];
- /* enable DMA stream again if needed */
- if (!(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT))
- hal2_start_adc(hal2);
- }
- buffer += size;
- ret += size;
- count -= size;
-
- DEBUG("(%d) ", size);
- }
- spin_unlock_irqrestore(&adc->lock, flags);
-out:
- DEBUG("\n");
-
- return ret;
-}
-
-/*
- * Add 'count' bytes from 'buffer' to DMA ring buffers. Return number of
- * bytes added or -EFAULT if copy_from_user failed.
- */
-static int hal2_add_buffer(struct hal2_card *hal2, char *buffer, int count)
-{
- unsigned long flags;
- unsigned char *buf;
- int size, ret = 0;
- struct hal2_desc *head;
- struct hal2_codec *dac = &hal2->dac;
-
- DEBUG("adding %d bytes ", count);
-
- spin_lock_irqsave(&dac->lock, flags);
- head = &dac->desc[dac->head];
- while (head->cnt == 0 && count > 0) {
- size = min((int)H2_BLOCK_SIZE, count);
- buf = &dac->buffer[dac->head * H2_BLOCK_SIZE];
- spin_unlock_irqrestore(&dac->lock, flags);
- if (copy_from_user(buf, buffer, size)) {
- ret = -EFAULT;
- goto out;
- }
- dma_sync_single(NULL, head->desc.pbuf, size, DMA_TO_DEVICE);
- spin_lock_irqsave(&dac->lock, flags);
- head->desc.cntinfo = size | HPCDMA_XIE;
- head->cnt = size;
- buffer += size;
- ret += size;
- count -= size;
- hal2_inc_head(dac);
- head = &dac->desc[dac->head];
-
- DEBUG("(%d) ", size);
- }
- if (!(dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) && ret > 0)
- hal2_start_dac(hal2);
- spin_unlock_irqrestore(&dac->lock, flags);
-out:
- DEBUG("\n");
-
- return ret;
-}
-
-#define hal2_reset_dac_pointer(hal2) hal2_reset_pointer(hal2, 1)
-#define hal2_reset_adc_pointer(hal2) hal2_reset_pointer(hal2, 0)
-static void hal2_reset_pointer(struct hal2_card *hal2, int is_dac)
-{
- int i;
- struct hal2_codec *codec = (is_dac) ? &hal2->dac : &hal2->adc;
-
- DEBUG("hal2_reset_pointer\n");
-
- for (i = 0; i < codec->desc_count; i++) {
- codec->desc[i].cnt = 0;
- codec->desc[i].desc.cntinfo = HPCDMA_XIE | (is_dac) ?
- HPCDMA_EOX : H2_BLOCK_SIZE;
- }
- codec->head = codec->tail = 0;
-}
-
-static int hal2_sync_dac(struct hal2_card *hal2)
-{
- DECLARE_WAITQUEUE(wait, current);
- struct hal2_codec *dac = &hal2->dac;
- int ret = 0;
- unsigned long flags;
- signed long timeout = 1000 * H2_BLOCK_SIZE * 2 * dac->voices *
- HZ / dac->sample_rate / 900;
-
- while (dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) {
- add_wait_queue(&dac->dma_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(timeout);
- spin_lock_irqsave(&dac->lock, flags);
- if (dac->desc[dac->tail].cnt)
- ret = -ETIME;
- spin_unlock_irqrestore(&dac->lock, flags);
- if (signal_pending(current))
- ret = -ERESTARTSYS;
- if (ret) {
- hal2_stop_dac(hal2);
- hal2_reset_dac_pointer(hal2);
- }
- remove_wait_queue(&dac->dma_wait, &wait);
- }
-
- return ret;
-}
-
-static int hal2_write_mixer(struct hal2_card *hal2, int index, int vol)
-{
- unsigned int l, r, tmp;
-
- DEBUG_MIX("mixer %d write\n", index);
-
- if (index >= SOUND_MIXER_NRDEVICES || !mixtable[index].avail)
- return -EINVAL;
-
- r = (vol >> 8) & 0xff;
- if (r > 100)
- r = 100;
- l = vol & 0xff;
- if (l > 100)
- l = 100;
-
- hal2->mixer.volume[mixtable[index].idx] = l | (r << 8);
-
- switch (mixtable[index].idx) {
- case H2_MIX_OUTPUT_ATT:
-
- DEBUG_MIX("output attenuator %d,%d\n", l, r);
-
- if (r | l) {
- tmp = hal2_i_look32(hal2, H2I_DAC_C2);
- tmp &= ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
-
- /* Attenuator has five bits */
- l = 31 * (100 - l) / 99;
- r = 31 * (100 - r) / 99;
-
- DEBUG_MIX("left: %d, right %d\n", l, r);
-
- tmp |= (l << H2I_C2_L_ATT_SHIFT) & H2I_C2_L_ATT_M;
- tmp |= (r << H2I_C2_R_ATT_SHIFT) & H2I_C2_R_ATT_M;
- hal2_i_write32(hal2, H2I_DAC_C2, tmp);
- } else
- hal2_i_setbit32(hal2, H2I_DAC_C2, H2I_C2_MUTE);
- break;
- case H2_MIX_INPUT_GAIN:
-
- DEBUG_MIX("input gain %d,%d\n", l, r);
-
- tmp = hal2_i_look32(hal2, H2I_ADC_C2);
- tmp &= ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
-
- /* Gain control has four bits */
- l = 16 * l / 100;
- r = 16 * r / 100;
-
- DEBUG_MIX("left: %d, right %d\n", l, r);
-
- tmp |= (l << H2I_C2_L_GAIN_SHIFT) & H2I_C2_L_GAIN_M;
- tmp |= (r << H2I_C2_R_GAIN_SHIFT) & H2I_C2_R_GAIN_M;
- hal2_i_write32(hal2, H2I_ADC_C2, tmp);
-
- break;
- }
-
- return 0;
-}
-
-static void hal2_init_mixer(struct hal2_card *hal2)
-{
- int i;
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (mixtable[i].avail)
- hal2->mixer.volume[mixtable[i].idx] = 100 | (100 << 8);
-
- /* disable attenuator */
- hal2_i_write32(hal2, H2I_DAC_C2, 0);
- /* set max input gain */
- hal2_i_write32(hal2, H2I_ADC_C2, H2I_C2_MUTE |
- (H2I_C2_L_GAIN_M << H2I_C2_L_GAIN_SHIFT) |
- (H2I_C2_R_GAIN_M << H2I_C2_R_GAIN_SHIFT));
- /* set max volume */
- hal2->mixer.master = 0xff;
- hal2->vol_regs->left = 0xff;
- hal2->vol_regs->right = 0xff;
-}
-
-/*
- * XXX: later i'll implement mixer for main volume which will be disabled
- * by default. enabling it users will be allowed to have master volume level
- * control on panel in their favourite X desktop
- */
-static void hal2_volume_control(int direction)
-{
- unsigned int master = hal2_card[0]->mixer.master;
- struct hal2_vol_regs *vol = hal2_card[0]->vol_regs;
-
- /* volume up */
- if (direction > 0 && master < 0xff)
- master++;
- /* volume down */
- else if (direction < 0 && master > 0)
- master--;
- /* TODO: mute/unmute */
- vol->left = master;
- vol->right = master;
- hal2_card[0]->mixer.master = master;
-}
-
-static int hal2_mixer_ioctl(struct hal2_card *hal2, unsigned int cmd,
- unsigned long arg)
-{
- int val;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
-
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, hal2str, sizeof(info.id));
- strlcpy(info.name, hal2str, sizeof(info.name));
- info.modify_counter = hal2->mixer.modcnt;
- if (copy_to_user((void *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
-
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, hal2str, sizeof(info.id));
- strlcpy(info.name, hal2str, sizeof(info.name));
- if (copy_to_user((void *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int *)arg);
-
- if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (_IOC_DIR(cmd) == _IOC_READ) {
- switch (_IOC_NR(cmd)) {
- /* Give the current record source */
- case SOUND_MIXER_RECSRC:
- val = 0; /* FIXME */
- break;
- /* Give the supported mixers, all of them support stereo */
- case SOUND_MIXER_DEVMASK:
- case SOUND_MIXER_STEREODEVS: {
- int i;
-
- for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (mixtable[i].avail)
- val |= 1 << i;
- break;
- }
- /* Arg contains a bit for each supported recording source */
- case SOUND_MIXER_RECMASK:
- val = 0;
- break;
- case SOUND_MIXER_CAPS:
- val = 0;
- break;
- /* Read a specific mixer */
- default: {
- int i = _IOC_NR(cmd);
-
- if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
- return -EINVAL;
- val = hal2->mixer.volume[mixtable[i].idx];
- break;
- }
- }
- return put_user(val, (int *)arg);
- }
-
- if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ))
- return -EINVAL;
-
- hal2->mixer.modcnt++;
-
- if (get_user(val, (int *)arg))
- return -EFAULT;
-
- switch (_IOC_NR(cmd)) {
- /* Arg contains a bit for each recording source */
- case SOUND_MIXER_RECSRC:
- return 0; /* FIXME */
- default:
- return hal2_write_mixer(hal2, _IOC_NR(cmd), val);
- }
-
- return 0;
-}
-
-static int hal2_open_mixdev(struct inode *inode, struct file *file)
-{
- struct hal2_card *hal2 = hal2_mixer_find_card(iminor(inode));
-
- if (hal2) {
- file->private_data = hal2;
- return nonseekable_open(inode, file);
- }
- return -ENODEV;
-}
-
-static int hal2_release_mixdev(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int hal2_ioctl_mixdev(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return hal2_mixer_ioctl((struct hal2_card *)file->private_data, cmd, arg);
-}
-
-static int hal2_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int val;
- struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *)arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return hal2_sync_dac(hal2);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_MULTI, (int *)arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_READ) {
- hal2_stop_adc(hal2);
- hal2_reset_adc_pointer(hal2);
- }
- if (file->f_mode & FMODE_WRITE) {
- hal2_stop_dac(hal2);
- hal2_reset_dac_pointer(hal2);
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *)arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- hal2_stop_adc(hal2);
- val = hal2_compute_rate(&hal2->adc, val);
- hal2->adc.sample_rate = val;
- hal2_set_adc_rate(hal2);
- }
- if (file->f_mode & FMODE_WRITE) {
- hal2_stop_dac(hal2);
- val = hal2_compute_rate(&hal2->dac, val);
- hal2->dac.sample_rate = val;
- hal2_set_dac_rate(hal2);
- }
- return put_user(val, (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *)arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- hal2_stop_adc(hal2);
- hal2->adc.voices = (val) ? 2 : 1;
- hal2_setup_adc(hal2);
- }
- if (file->f_mode & FMODE_WRITE) {
- hal2_stop_dac(hal2);
- hal2->dac.voices = (val) ? 2 : 1;
- hal2_setup_dac(hal2);
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *)arg))
- return -EFAULT;
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- hal2_stop_adc(hal2);
- hal2->adc.voices = (val == 1) ? 1 : 2;
- hal2_setup_adc(hal2);
- }
- if (file->f_mode & FMODE_WRITE) {
- hal2_stop_dac(hal2);
- hal2->dac.voices = (val == 1) ? 1 : 2;
- hal2_setup_dac(hal2);
- }
- }
- val = -EINVAL;
- if (file->f_mode & FMODE_READ)
- val = hal2->adc.voices;
- if (file->f_mode & FMODE_WRITE)
- val = hal2->dac.voices;
- return put_user(val, (int *)arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(H2_SUPPORTED_FORMATS, (int *)arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
- if (get_user(val, (int *)arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (!(val & H2_SUPPORTED_FORMATS))
- return -EINVAL;
- if (file->f_mode & FMODE_READ) {
- hal2_stop_adc(hal2);
- hal2->adc.format = val;
- hal2_setup_adc(hal2);
- }
- if (file->f_mode & FMODE_WRITE) {
- hal2_stop_dac(hal2);
- hal2->dac.format = val;
- hal2_setup_dac(hal2);
- }
- } else {
- val = -EINVAL;
- if (file->f_mode & FMODE_READ)
- val = hal2->adc.format;
- if (file->f_mode & FMODE_WRITE)
- val = hal2->dac.format;
- }
- return put_user(val, (int *)arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETOSPACE: {
- audio_buf_info info;
- int i;
- unsigned long flags;
- struct hal2_codec *dac = &hal2->dac;
-
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- info.fragments = 0;
- spin_lock_irqsave(&dac->lock, flags);
- for (i = 0; i < dac->desc_count; i++)
- if (dac->desc[i].cnt == 0)
- info.fragments++;
- spin_unlock_irqrestore(&dac->lock, flags);
- info.fragstotal = dac->desc_count;
- info.fragsize = H2_BLOCK_SIZE;
- info.bytes = info.fragsize * info.fragments;
-
- return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0;
- }
-
- case SNDCTL_DSP_GETISPACE: {
- audio_buf_info info;
- int i;
- unsigned long flags;
- struct hal2_codec *adc = &hal2->adc;
-
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- info.fragments = 0;
- info.bytes = 0;
- spin_lock_irqsave(&adc->lock, flags);
- for (i = 0; i < adc->desc_count; i++)
- if (adc->desc[i].cnt > 0) {
- info.fragments++;
- info.bytes += adc->desc[i].cnt;
- }
- spin_unlock_irqrestore(&adc->lock, flags);
- info.fragstotal = adc->desc_count;
- info.fragsize = H2_BLOCK_SIZE;
-
- return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0;
- }
-
- case SNDCTL_DSP_NONBLOCK:
- file->f_flags |= O_NONBLOCK;
- return 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- return put_user(H2_BLOCK_SIZE, (int *)arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- return 0;
-
- case SOUND_PCM_READ_RATE:
- val = -EINVAL;
- if (file->f_mode & FMODE_READ)
- val = hal2->adc.sample_rate;
- if (file->f_mode & FMODE_WRITE)
- val = hal2->dac.sample_rate;
- return put_user(val, (int *)arg);
-
- case SOUND_PCM_READ_CHANNELS:
- val = -EINVAL;
- if (file->f_mode & FMODE_READ)
- val = hal2->adc.voices;
- if (file->f_mode & FMODE_WRITE)
- val = hal2->dac.voices;
- return put_user(val, (int *)arg);
-
- case SOUND_PCM_READ_BITS:
- return put_user(16, (int *)arg);
- }
-
- return hal2_mixer_ioctl(hal2, cmd, arg);
-}
-
-static ssize_t hal2_read(struct file *file, char *buffer,
- size_t count, loff_t *ppos)
-{
- ssize_t err;
- struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
- struct hal2_codec *adc = &hal2->adc;
-
- if (!count)
- return 0;
- if (mutex_lock_interruptible(&adc->sem))
- return -EINTR;
- if (file->f_flags & O_NONBLOCK) {
- err = hal2_get_buffer(hal2, buffer, count);
- err = err == 0 ? -EAGAIN : err;
- } else {
- do {
- /* ~10% longer */
- signed long timeout = 1000 * H2_BLOCK_SIZE *
- 2 * adc->voices * HZ / adc->sample_rate / 900;
- unsigned long flags;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t cnt = 0;
-
- err = hal2_get_buffer(hal2, buffer, count);
- if (err > 0) {
- count -= err;
- cnt += err;
- buffer += err;
- err = cnt;
- }
- if (count > 0 && err >= 0) {
- add_wait_queue(&adc->dma_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(timeout);
- spin_lock_irqsave(&adc->lock, flags);
- if (!adc->desc[adc->tail].cnt)
- err = -EAGAIN;
- spin_unlock_irqrestore(&adc->lock, flags);
- if (signal_pending(current))
- err = -ERESTARTSYS;
- remove_wait_queue(&adc->dma_wait, &wait);
- if (err < 0) {
- hal2_stop_adc(hal2);
- hal2_reset_adc_pointer(hal2);
- }
- }
- } while (count > 0 && err >= 0);
- }
- mutex_unlock(&adc->sem);
-
- return err;
-}
-
-static ssize_t hal2_write(struct file *file, const char *buffer,
- size_t count, loff_t *ppos)
-{
- ssize_t err;
- char *buf = (char*) buffer;
- struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
- struct hal2_codec *dac = &hal2->dac;
-
- if (!count)
- return 0;
- if (mutex_lock_interruptible(&dac->sem))
- return -EINTR;
- if (file->f_flags & O_NONBLOCK) {
- err = hal2_add_buffer(hal2, buf, count);
- err = err == 0 ? -EAGAIN : err;
- } else {
- do {
- /* ~10% longer */
- signed long timeout = 1000 * H2_BLOCK_SIZE *
- 2 * dac->voices * HZ / dac->sample_rate / 900;
- unsigned long flags;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t cnt = 0;
-
- err = hal2_add_buffer(hal2, buf, count);
- if (err > 0) {
- count -= err;
- cnt += err;
- buf += err;
- err = cnt;
- }
- if (count > 0 && err >= 0) {
- add_wait_queue(&dac->dma_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(timeout);
- spin_lock_irqsave(&dac->lock, flags);
- if (dac->desc[dac->head].cnt)
- err = -EAGAIN;
- spin_unlock_irqrestore(&dac->lock, flags);
- if (signal_pending(current))
- err = -ERESTARTSYS;
- remove_wait_queue(&dac->dma_wait, &wait);
- if (err < 0) {
- hal2_stop_dac(hal2);
- hal2_reset_dac_pointer(hal2);
- }
- }
- } while (count > 0 && err >= 0);
- }
- mutex_unlock(&dac->sem);
-
- return err;
-}
-
-static unsigned int hal2_poll(struct file *file, struct poll_table_struct *wait)
-{
- unsigned long flags;
- unsigned int mask = 0;
- struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-
- if (file->f_mode & FMODE_READ) {
- struct hal2_codec *adc = &hal2->adc;
-
- poll_wait(file, &adc->dma_wait, wait);
- spin_lock_irqsave(&adc->lock, flags);
- if (adc->desc[adc->tail].cnt > 0)
- mask |= POLLIN;
- spin_unlock_irqrestore(&adc->lock, flags);
- }
-
- if (file->f_mode & FMODE_WRITE) {
- struct hal2_codec *dac = &hal2->dac;
-
- poll_wait(file, &dac->dma_wait, wait);
- spin_lock_irqsave(&dac->lock, flags);
- if (dac->desc[dac->head].cnt == 0)
- mask |= POLLOUT;
- spin_unlock_irqrestore(&dac->lock, flags);
- }
-
- return mask;
-}
-
-static int hal2_open(struct inode *inode, struct file *file)
-{
- int err;
- struct hal2_card *hal2 = hal2_dsp_find_card(iminor(inode));
-
- if (!hal2)
- return -ENODEV;
- file->private_data = hal2;
- if (file->f_mode & FMODE_READ) {
- struct hal2_codec *adc = &hal2->adc;
-
- if (adc->usecount)
- return -EBUSY;
- /* OSS spec wanted us to use 8 bit, 8 kHz mono by default,
- * but HAL2 can't do 8bit audio */
- adc->format = AFMT_S16_BE;
- adc->voices = 1;
- adc->sample_rate = hal2_compute_rate(adc, 8000);
- hal2_set_adc_rate(hal2);
- err = hal2_alloc_adc_dmabuf(adc);
- if (err)
- return err;
- hal2_setup_adc(hal2);
- adc->usecount++;
- }
- if (file->f_mode & FMODE_WRITE) {
- struct hal2_codec *dac = &hal2->dac;
-
- if (dac->usecount)
- return -EBUSY;
- dac->format = AFMT_S16_BE;
- dac->voices = 1;
- dac->sample_rate = hal2_compute_rate(dac, 8000);
- hal2_set_dac_rate(hal2);
- err = hal2_alloc_dac_dmabuf(dac);
- if (err)
- return err;
- hal2_setup_dac(hal2);
- dac->usecount++;
- }
-
- return nonseekable_open(inode, file);
-}
-
-static int hal2_release(struct inode *inode, struct file *file)
-{
- struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-
- if (file->f_mode & FMODE_READ) {
- struct hal2_codec *adc = &hal2->adc;
-
- mutex_lock(&adc->sem);
- hal2_stop_adc(hal2);
- hal2_free_adc_dmabuf(adc);
- adc->usecount--;
- mutex_unlock(&adc->sem);
- }
- if (file->f_mode & FMODE_WRITE) {
- struct hal2_codec *dac = &hal2->dac;
-
- mutex_lock(&dac->sem);
- hal2_sync_dac(hal2);
- hal2_free_dac_dmabuf(dac);
- dac->usecount--;
- mutex_unlock(&dac->sem);
- }
-
- return 0;
-}
-
-static const struct file_operations hal2_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = hal2_read,
- .write = hal2_write,
- .poll = hal2_poll,
- .ioctl = hal2_ioctl,
- .open = hal2_open,
- .release = hal2_release,
-};
-
-static const struct file_operations hal2_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .ioctl = hal2_ioctl_mixdev,
- .open = hal2_open_mixdev,
- .release = hal2_release_mixdev,
-};
-
-static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3,
- int index)
-{
- codec->pbus.pbusnr = index;
- codec->pbus.pbus = &hpc3->pbdma[index];
- init_waitqueue_head(&codec->dma_wait);
- mutex_init(&codec->sem);
- spin_lock_init(&codec->lock);
-}
-
-static int hal2_detect(struct hal2_card *hal2)
-{
- unsigned short board, major, minor;
- unsigned short rev;
-
- /* reset HAL2 */
- hal2_isr_write(hal2, 0);
- /* release reset */
- hal2_isr_write(hal2, H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N);
-
- hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);
- if ((rev = hal2_rev_look(hal2)) & H2_REV_AUDIO_PRESENT)
- return -ENODEV;
-
- board = (rev & H2_REV_BOARD_M) >> 12;
- major = (rev & H2_REV_MAJOR_CHIP_M) >> 4;
- minor = (rev & H2_REV_MINOR_CHIP_M);
-
- printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n",
- board, major, minor);
-
- return 0;
-}
-
-static int hal2_init_card(struct hal2_card **phal2, struct hpc3_regs *hpc3)
-{
- int ret = 0;
- struct hal2_card *hal2;
-
- hal2 = kzalloc(sizeof(struct hal2_card), GFP_KERNEL);
- if (!hal2)
- return -ENOMEM;
-
- hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0];
- hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1];
- hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2];
- hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3];
-
- if (hal2_detect(hal2) < 0) {
- ret = -ENODEV;
- goto free_card;
- }
-
- hal2_init_codec(&hal2->dac, hpc3, 0);
- hal2_init_codec(&hal2->adc, hpc3, 1);
-
- /*
- * All DMA channel interfaces in HAL2 are designed to operate with
- * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles
- * in D5. HAL2 is a 16-bit device which can accept both big and little
- * endian format. It assumes that even address bytes are on high
- * portion of PBUS (15:8) and assumes that HPC3 is programmed to
- * accept a live (unsynchronized) version of P_DREQ_N from HAL2.
- */
-#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \
- (2 << HPC3_DMACFG_D4R_SHIFT) | \
- (2 << HPC3_DMACFG_D5R_SHIFT) | \
- (0 << HPC3_DMACFG_D3W_SHIFT) | \
- (2 << HPC3_DMACFG_D4W_SHIFT) | \
- (2 << HPC3_DMACFG_D5W_SHIFT) | \
- HPC3_DMACFG_DS16 | \
- HPC3_DMACFG_EVENHI | \
- HPC3_DMACFG_RTIME | \
- (8 << HPC3_DMACFG_BURST_SHIFT) | \
- HPC3_DMACFG_DRQLIVE)
- /*
- * Ignore what's mentioned in the specification and write value which
- * works in The Real World (TM)
- */
- hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
- hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
-
- if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
- hal2str, hal2)) {
- printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
- ret = -EAGAIN;
- goto free_card;
- }
-
- hal2->dev_dsp = register_sound_dsp(&hal2_audio_fops, -1);
- if (hal2->dev_dsp < 0) {
- ret = hal2->dev_dsp;
- goto free_irq;
- }
-
- hal2->dev_mixer = register_sound_mixer(&hal2_mixer_fops, -1);
- if (hal2->dev_mixer < 0) {
- ret = hal2->dev_mixer;
- goto unregister_dsp;
- }
-
- hal2_init_mixer(hal2);
-
- *phal2 = hal2;
- return 0;
-unregister_dsp:
- unregister_sound_dsp(hal2->dev_dsp);
-free_irq:
- free_irq(SGI_HPCDMA_IRQ, hal2);
-free_card:
- kfree(hal2);
-
- return ret;
-}
-
-extern void (*indy_volume_button)(int);
-
-/*
- * Assuming only one HAL2 card. Mail me if you ever meet machine with
- * more than one.
- */
-static int __init init_hal2(void)
-{
- int i, error;
-
- for (i = 0; i < MAXCARDS; i++)
- hal2_card[i] = NULL;
-
- error = hal2_init_card(&hal2_card[0], hpc3c0);
-
- /* let Indy's volume buttons work */
- if (!error && !ip22_is_fullhouse())
- indy_volume_button = hal2_volume_control;
-
- return error;
-
-}
-
-static void __exit exit_hal2(void)
-{
- int i;
-
- /* unregister volume butons callback function */
- indy_volume_button = NULL;
-
- for (i = 0; i < MAXCARDS; i++)
- if (hal2_card[i]) {
- free_irq(SGI_HPCDMA_IRQ, hal2_card[i]);
- unregister_sound_dsp(hal2_card[i]->dev_dsp);
- unregister_sound_mixer(hal2_card[i]->dev_mixer);
- kfree(hal2_card[i]);
- }
-}
-
-module_init(init_hal2);
-module_exit(exit_hal2);
-
-MODULE_DESCRIPTION("OSS compatible driver for SGI HAL2 audio");
-MODULE_AUTHOR("Ladislav Michl");
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
index a690ca57adb..6c0a770ed05 100644
--- a/sound/oss/mpu401.c
+++ b/sound/oss/mpu401.c
@@ -1015,7 +1015,7 @@ int attach_mpu401(struct address_info *hw_config, struct module *owner)
mpu401_chk_version(m, devc);
if (devc->version == 0)
mpu401_chk_version(m, devc);
- spin_unlock_irqrestore(&devc->lock,flags);
+ spin_unlock_irqrestore(&devc->lock, flags);
}
if (devc->version != 0)
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
index ba38d620009..e4282d93a1a 100644
--- a/sound/oss/msnd.c
+++ b/sound/oss/msnd.c
@@ -20,8 +20,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $
- *
********************************************************************/
#include <linux/module.h>
diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h
index d0ca582c458..61b3955481c 100644
--- a/sound/oss/msnd.h
+++ b/sound/oss/msnd.h
@@ -24,8 +24,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $
- *
********************************************************************/
#ifndef __MSND_H
#define __MSND_H
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
index 7ffea5267f9..1a17dde2f65 100644
--- a/sound/oss/msnd_classic.h
+++ b/sound/oss/msnd_classic.h
@@ -24,8 +24,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $
- *
********************************************************************/
#ifndef __MSND_CLASSIC_H
#define __MSND_CLASSIC_H
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index f1f49ebf752..bf27e008f46 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -29,13 +29,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $
- *
* 12-3-2000 Modified IO port validation Steve Sycamore
*
- *
- * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $
- *
********************************************************************/
#include <linux/kernel.h>
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
index cce91148700..c18d66cbbe3 100644
--- a/sound/oss/msnd_pinnacle.h
+++ b/sound/oss/msnd_pinnacle.h
@@ -24,8 +24,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $
- *
********************************************************************/
#ifndef __MSND_PINNACLE_H
#define __MSND_PINNACLE_H
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
index a9c23b2502a..61aaedae6b7 100644
--- a/sound/oss/soundcard.c
+++ b/sound/oss/soundcard.c
@@ -561,7 +561,7 @@ static int __init oss_init(void)
for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR, dev_list[i].minor),
+ MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL,
"%s", dev_list[i].name);
if (!dev_list[i].num)
@@ -569,8 +569,9 @@ static int __init oss_init(void)
for (j = 1; j < *dev_list[i].num; j++)
device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)),
- "%s%d", dev_list[i].name, j);
+ MKDEV(SOUND_MAJOR,
+ dev_list[i].minor + (j*0x10)),
+ NULL, "%s%d", dev_list[i].name, j);
}
if (sound_nblocks >= 1024)
diff --git a/sound/oss/trident.c b/sound/oss/trident.c
deleted file mode 100644
index f43f91ef86c..00000000000
--- a/sound/oss/trident.c
+++ /dev/null
@@ -1,4654 +0,0 @@
-/*
- * OSS driver for Linux 2.[46].x for
- *
- * Trident 4D-Wave
- * SiS 7018
- * ALi 5451
- * Tvia/IGST CyberPro 5050
- *
- * Driver: Alan Cox <alan@redhat.com>
- *
- * Built from:
- * Low level code: <audio@tridentmicro.com> from ALSA
- * Framework: Thomas Sailer <sailer@ife.ee.ethz.ch>
- * Extended by: Zach Brown <zab@redhat.com>
- *
- * Hacked up by:
- * Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
- * Ollie Lho <ollie@sis.com.tw> SiS 7018 Audio Core Support
- * Ching-Ling Lee <cling-li@ali.com.tw> ALi 5451 Audio Core Support
- * Matt Wu <mattwu@acersoftech.com.cn> ALi 5451 Audio Core Support
- * Peter Wächtler <pwaechtler@loewe-komp.de> CyberPro5050 support
- * Muli Ben-Yehuda <mulix@mulix.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * History
- * v0.14.10j
- * January 3 2004 Eugene Teo <eugeneteo@eugeneteo.net>
- * minor cleanup to use pr_debug instead of TRDBG since it is already
- * defined in linux/kernel.h.
- * v0.14.10i
- * December 29 2003 Muli Ben-Yehuda <mulix@mulix.org>
- * major cleanup for 2.6, fix a few error patch buglets
- * with returning without properly cleaning up first,
- * get rid of lock_kernel().
- * v0.14.10h
- * Sept 10 2002 Pascal Schmidt <der.eremit@email.de>
- * added support for ALi 5451 joystick port
- * v0.14.10g
- * Sept 05 2002 Alan Cox <alan@redhat.com>
- * adapt to new pci joystick attachment interface
- * v0.14.10f
- * July 24 2002 Muli Ben-Yehuda <mulix@actcom.co.il>
- * patch from Eric Lemar (via Ian Soboroff): in suspend and resume,
- * fix wrong cast from pci_dev* to struct trident_card*.
- * v0.14.10e
- * July 19 2002 Muli Ben-Yehuda <mulix@actcom.co.il>
- * rewrite the DMA buffer allocation/deallcoation functions, to make it
- * modular and fix a bug where we would call free_pages on memory
- * obtained with pci_alloc_consistent. Also remove unnecessary #ifdef
- * CONFIG_PROC_FS and various other cleanups.
- * v0.14.10d
- * July 19 2002 Muli Ben-Yehuda <mulix@actcom.co.il>
- * made several printk(KERN_NOTICE...) into TRDBG(...), to avoid spamming
- * my syslog with hundreds of messages.
- * v0.14.10c
- * July 16 2002 Muli Ben-Yehuda <mulix@actcom.co.il>
- * Cleaned up Lei Hu's 0.4.10 driver to conform to Documentation/CodingStyle
- * and the coding style used in the rest of the file.
- * v0.14.10b
- * June 23 2002 Muli Ben-Yehuda <mulix@actcom.co.il>
- * add a missing unlock_set_fmt, remove a superflous lock/unlock pair
- * with nothing in between.
- * v0.14.10a
- * June 21 2002 Muli Ben-Yehuda <mulix@actcom.co.il>
- * use a debug macro instead of #ifdef CONFIG_DEBUG, trim to 80 columns
- * per line, use 'do {} while (0)' in statement macros.
- * v0.14.10
- * June 6 2002 Lei Hu <Lei_hu@ali.com.tw>
- * rewrite the part to read/write registers of audio codec for Ali5451
- * v0.14.9e
- * January 2 2002 Vojtech Pavlik <vojtech@ucw.cz> added gameport
- * support to avoid resource conflict with pcigame.c
- * v0.14.9d
- * October 8 2001 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- * use set_current_state, properly release resources on failure in
- * trident_probe, get rid of check_region
- * v0.14.9c
- * August 10 2001 Peter Wächtler <pwaechtler@loewe-komp.de>
- * added support for Tvia (formerly Integraphics/IGST) CyberPro5050
- * this chip is often found in settop boxes (combined video+audio)
- * v0.14.9b
- * Switch to static inline not extern inline (gcc 3)
- * v0.14.9a
- * Aug 6 2001 Alan Cox
- * 0.14.9 crashed on rmmod due to a timer/bh left running. Simplified
- * the existing logic (the BH doesn't help as ac97 is lock_irqsave)
- * and used del_timer_sync to clean up
- * Fixed a problem where the ALi change broke my generic card
- * v0.14.9
- * Jul 10 2001 Matt Wu
- * Add H/W Volume Control
- * v0.14.8a
- * July 7 2001 Alan Cox
- * Moved Matt Wu's ac97 register cache into the card structure
- * v0.14.8
- * Apr 30 2001 Matt Wu
- * Set EBUF1 and EBUF2 to still mode
- * Add dc97/ac97 reset function
- * Fix power management: ali_restore_regs
- * unreleased
- * Mar 09 2001 Matt Wu
- * Add cache for ac97 access
- * v0.14.7
- * Feb 06 2001 Matt Wu
- * Fix ac97 initialization
- * Fix bug: an extra tail will be played when playing
- * Jan 05 2001 Matt Wu
- * Implement multi-channels and S/PDIF in support for ALi 1535+
- * v0.14.6
- * Nov 1 2000 Ching-Ling Lee
- * Fix the bug of memory leak when switching 5.1-channels to 2 channels.
- * Add lock protection into dynamic changing format of data.
- * Oct 18 2000 Ching-Ling Lee
- * 5.1-channels support for ALi
- * June 28 2000 Ching-Ling Lee
- * S/PDIF out/in(playback/record) support for ALi 1535+, using /proc to be selected by user
- * Simple Power Management support for ALi
- * v0.14.5 May 23 2000 Ollie Lho
- * Misc bug fix from the Net
- * v0.14.4 May 20 2000 Aaron Holtzman
- * Fix kfree'd memory access in release
- * Fix race in open while looking for a free virtual channel slot
- * remove open_wait wq (which appears to be unused)
- * v0.14.3 May 10 2000 Ollie Lho
- * fixed a small bug in trident_update_ptr, xmms 1.0.1 no longer uses 100% CPU
- * v0.14.2 Mar 29 2000 Ching-Ling Lee
- * Add clear to silence advance in trident_update_ptr
- * fix invalid data of the end of the sound
- * v0.14.1 Mar 24 2000 Ching-Ling Lee
- * ALi 5451 support added, playback and recording O.K.
- * ALi 5451 originally developed and structured based on sonicvibes, and
- * suggested to merge into this file by Alan Cox.
- * v0.14 Mar 15 2000 Ollie Lho
- * 5.1 channel output support with channel binding. What's the Matrix ?
- * v0.13.1 Mar 10 2000 Ollie Lho
- * few minor bugs on dual codec support, needs more testing
- * v0.13 Mar 03 2000 Ollie Lho
- * new pci_* for 2.4 kernel, back ported to 2.2
- * v0.12 Feb 23 2000 Ollie Lho
- * Preliminary Recording support
- * v0.11.2 Feb 19 2000 Ollie Lho
- * removed incomplete full-dulplex support
- * v0.11.1 Jan 28 2000 Ollie Lho
- * small bug in setting sample rate for 4d-nx (reported by Aaron)
- * v0.11 Jan 27 2000 Ollie Lho
- * DMA bug, scheduler latency, second try
- * v0.10 Jan 24 2000 Ollie Lho
- * DMA bug fixed, found kernel scheduling problem
- * v0.09 Jan 20 2000 Ollie Lho
- * Clean up of channel register access routine (prepare for channel binding)
- * v0.08 Jan 14 2000 Ollie Lho
- * Isolation of AC97 codec code
- * v0.07 Jan 13 2000 Ollie Lho
- * Get rid of ugly old low level access routines (e.g. CHRegs.lp****)
- * v0.06 Jan 11 2000 Ollie Lho
- * Preliminary support for dual (more ?) AC97 codecs
- * v0.05 Jan 08 2000 Luca Montecchiani <m.luca@iname.com>
- * adapt to 2.3.x new __setup/__init call
- * v0.04 Dec 31 1999 Ollie Lho
- * Multiple Open, using Middle Loop Interrupt to smooth playback
- * v0.03 Dec 24 1999 Ollie Lho
- * mem leak in prog_dmabuf and dealloc_dmabuf removed
- * v0.02 Dec 15 1999 Ollie Lho
- * SiS 7018 support added, playback O.K.
- * v0.01 Alan Cox et. al.
- * Initial Release in kernel 2.3.30, does not work
- *
- * ToDo
- * Clean up of low level channel register access code. (done)
- * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done)
- * Dual AC97 codecs support (done)
- * Recording support (done)
- * Mmap support
- * "Channel Binding" ioctl extension (done)
- * new pci device driver interface for 2.4 kernel (done)
- *
- * Lock order (high->low)
- * lock - hardware lock
- * open_mutex - guard opens
- * sem - guard dmabuf, write re-entry etc
- */
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/sound.h>
-#include <linux/slab.h>
-#include <linux/soundcard.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/poll.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
-#include <linux/bitops.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/pm.h>
-#include <linux/gameport.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/mm.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#if defined(CONFIG_ALPHA_NAUTILUS) || defined(CONFIG_ALPHA_GENERIC)
-#include <asm/hwrpb.h>
-#endif
-
-#include "trident.h"
-
-#define DRIVER_VERSION "0.14.10j-2.6"
-
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
-#define SUPPORT_JOYSTICK 1
-#endif
-
-/* magic numbers to protect our data structures */
-#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
-#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */
-
-#define TRIDENT_DMA_MASK 0x3fffffff /* DMA buffer mask for pci_alloc_consist */
-#define ALI_DMA_MASK 0x7fffffff /* ALI Tridents have 31-bit DMA. Wow. */
-
-#define NR_HW_CH 32
-
-/* maximum number of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only
- have 2 SDATA_IN lines (currently) */
-#define NR_AC97 2
-
-/* minor number of /dev/swmodem (temporary, experimental) */
-#define SND_DEV_SWMODEM 7
-
-static const unsigned ali_multi_channels_5_1[] = {
- /*ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL, */
- ALI_CENTER_CHANNEL,
- ALI_LEF_CHANNEL,
- ALI_SURR_LEFT_CHANNEL,
- ALI_SURR_RIGHT_CHANNEL
-};
-
-static const unsigned sample_size[] = { 1, 2, 2, 4 };
-static const unsigned sample_shift[] = { 0, 1, 1, 2 };
-
-static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n";
-
-enum {
- TRIDENT_4D_DX = 0,
- TRIDENT_4D_NX,
- SIS_7018,
- ALI_5451,
- CYBER5050
-};
-
-static char *card_names[] = {
- "Trident 4DWave DX",
- "Trident 4DWave NX",
- "SiS 7018 PCI Audio",
- "ALi Audio Accelerator",
- "Tvia/IGST CyberPro 5050"
-};
-
-static struct pci_device_id trident_pci_tbl[] = {
- {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX),
- PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TRIDENT_4D_DX},
- {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX),
- 0, 0, TRIDENT_4D_NX},
- {PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018), 0, 0, SIS_7018},
- {PCI_DEVICE(PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451), 0, 0, ALI_5451},
- {PCI_DEVICE(PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050),
- 0, 0, CYBER5050},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, trident_pci_tbl);
-
-/* "software" or virtual channel, an instance of opened /dev/dsp */
-struct trident_state {
- unsigned int magic;
- struct trident_card *card; /* Card info */
-
- /* file mode */
- mode_t open_mode;
-
- /* virtual channel number */
- int virt;
-
- struct dmabuf {
- /* wave sample stuff */
- unsigned int rate;
- unsigned char fmt, enable;
-
- /* hardware channel */
- struct trident_channel *channel;
-
- /* OSS buffer management stuff */
- void *rawbuf;
- dma_addr_t dma_handle;
- unsigned buforder;
- unsigned numfrag;
- unsigned fragshift;
-
- /* our buffer acts like a circular ring */
- unsigned hwptr; /* where dma last started, updated by update_ptr */
- unsigned swptr; /* where driver last clear/filled, updated by read/write */
- int count; /* bytes to be comsumed or been generated by dma machine */
- unsigned total_bytes; /* total bytes dmaed by hardware */
-
- unsigned error; /* number of over/underruns */
- /* put process on wait queue when no more space in buffer */
- wait_queue_head_t wait;
-
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dmasize;
- unsigned fragsamples;
-
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned endcleared:1;
- unsigned update_flag;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
-
- } dmabuf;
-
- /* 5.1 channels */
- struct trident_state *other_states[4];
- int multi_channels_adjust_count;
- unsigned chans_num;
- unsigned long fmt_flag;
- /* Guard against mmap/write/read races */
- struct mutex sem;
-
-};
-
-/* hardware channels */
-struct trident_channel {
- int num; /* channel number */
- u32 lba; /* Loop Begine Address, where dma buffer starts */
- u32 eso; /* End Sample Offset, wehre dma buffer ends */
- /* (in the unit of samples) */
- u32 delta; /* delta value, sample rate / 48k for playback, */
- /* 48k/sample rate for recording */
- u16 attribute; /* control where PCM data go and come */
- u16 fm_vol;
- u32 control; /* signed/unsigned, 8/16 bits, mono/stereo */
-};
-
-struct trident_pcm_bank_address {
- u32 start;
- u32 stop;
- u32 aint;
- u32 aint_en;
-};
-
-static struct trident_pcm_bank_address bank_a_addrs = {
- T4D_START_A,
- T4D_STOP_A,
- T4D_AINT_A,
- T4D_AINTEN_A
-};
-
-static struct trident_pcm_bank_address bank_b_addrs = {
- T4D_START_B,
- T4D_STOP_B,
- T4D_AINT_B,
- T4D_AINTEN_B
-};
-
-struct trident_pcm_bank {
- /* register addresses to control bank operations */
- struct trident_pcm_bank_address *addresses;
- /* each bank has 32 channels */
- u32 bitmap; /* channel allocation bitmap */
- struct trident_channel channels[32];
-};
-
-struct trident_card {
- unsigned int magic;
-
- /* We keep trident cards in a linked list */
- struct trident_card *next;
-
- /* single open lock mechanism, only used for recording */
- struct mutex open_mutex;
-
- /* The trident has a certain amount of cross channel interaction
- so we use a single per card lock */
- spinlock_t lock;
-
- /* PCI device stuff */
- struct pci_dev *pci_dev;
- u16 pci_id;
- u8 revision;
-
- /* soundcore stuff */
- int dev_audio;
-
- /* structures for abstraction of hardware facilities, codecs, */
- /* banks and channels */
- struct ac97_codec *ac97_codec[NR_AC97];
- struct trident_pcm_bank banks[NR_BANKS];
- struct trident_state *states[NR_HW_CH];
-
- /* hardware resources */
- unsigned long iobase;
- u32 irq;
-
- /* Function support */
- struct trident_channel *(*alloc_pcm_channel) (struct trident_card *);
- struct trident_channel *(*alloc_rec_pcm_channel) (struct trident_card *);
- void (*free_pcm_channel) (struct trident_card *, unsigned int chan);
- void (*address_interrupt) (struct trident_card *);
-
- /* Added by Matt Wu 01-05-2001 for spdif in */
- int multi_channel_use_count;
- int rec_channel_use_count;
- u16 mixer_regs[64][NR_AC97]; /* Made card local by Alan */
- int mixer_regs_ready;
-
- /* Added for hardware volume control */
- int hwvolctl;
- struct timer_list timer;
-
- /* Game port support */
- struct gameport *gameport;
-};
-
-enum dmabuf_mode {
- DM_PLAYBACK = 0,
- DM_RECORD
-};
-
-/* table to map from CHANNELMASK to channel attribute for SiS 7018 */
-static u16 mask2attr[] = {
- PCM_LR, PCM_LR, SURR_LR, CENTER_LFE,
- HSET, MIC, MODEM_LINE1, MODEM_LINE2,
- I2S_LR, SPDIF_LR
-};
-
-/* table to map from channel attribute to CHANNELMASK for SiS 7018 */
-static int attr2mask[] = {
- DSP_BIND_MODEM1, DSP_BIND_MODEM2, DSP_BIND_FRONT, DSP_BIND_HANDSET,
- DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF
-};
-
-/* Added by Matt Wu 01-05-2001 for spdif in */
-static int ali_close_multi_channels(void);
-static void ali_delay(struct trident_card *card, int interval);
-static void ali_detect_spdif_rate(struct trident_card *card);
-
-static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val);
-static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg);
-
-static struct trident_card *devs;
-
-static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
-static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg);
-
-static int trident_open_mixdev(struct inode *inode, struct file *file);
-static int trident_ioctl_mixdev(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
-
-static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val);
-static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg);
-static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate);
-static void ali_enable_special_channel(struct trident_state *stat);
-static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card);
-static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card);
-static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel);
-static int ali_setup_multi_channels(struct trident_card *card, int chan_nums);
-static unsigned int ali_get_spdif_in_rate(struct trident_card *card);
-static void ali_setup_spdif_in(struct trident_card *card);
-static void ali_disable_spdif_in(struct trident_card *card);
-static void ali_disable_special_channel(struct trident_card *card, int ch);
-static void ali_setup_spdif_out(struct trident_card *card, int flag);
-static int ali_write_5_1(struct trident_state *state,
- const char __user *buffer,
- int cnt_for_multi_channel, unsigned int *copy_count,
- unsigned int *state_cnt);
-static int ali_allocate_other_states_resources(struct trident_state *state,
- int chan_nums);
-static void ali_free_other_states_resources(struct trident_state *state);
-
-#define seek_offset(dma_ptr, buffer, cnt, offset, copy_count) do { \
- (dma_ptr) += (offset); \
- (buffer) += (offset); \
- (cnt) -= (offset); \
- (copy_count) += (offset); \
-} while (0)
-
-static inline int lock_set_fmt(struct trident_state* state)
-{
- if (test_and_set_bit(0, &state->fmt_flag))
- return -EFAULT;
-
- return 0;
-}
-
-static inline void unlock_set_fmt(struct trident_state* state)
-{
- clear_bit(0, &state->fmt_flag);
-}
-
-static int
-trident_enable_loop_interrupts(struct trident_card *card)
-{
- u32 global_control;
-
- global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR));
-
- switch (card->pci_id) {
- case PCI_DEVICE_ID_SI_7018:
- global_control |= (ENDLP_IE | MIDLP_IE | BANK_B_EN);
- break;
- case PCI_DEVICE_ID_ALI_5451:
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- case PCI_DEVICE_ID_INTERG_5050:
- global_control |= (ENDLP_IE | MIDLP_IE);
- break;
- default:
- return 0;
- }
-
- outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR));
-
- pr_debug("trident: Enable Loop Interrupts, globctl = 0x%08X\n",
- inl(TRID_REG(card, T4D_LFO_GC_CIR)));
-
- return 1;
-}
-
-static int
-trident_disable_loop_interrupts(struct trident_card *card)
-{
- u32 global_control;
-
- global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR));
- global_control &= ~(ENDLP_IE | MIDLP_IE);
- outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR));
-
- pr_debug("trident: Disabled Loop Interrupts, globctl = 0x%08X\n",
- global_control);
-
- return 1;
-}
-
-static void
-trident_enable_voice_irq(struct trident_card *card, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
- struct trident_pcm_bank *bank = &card->banks[channel >> 5];
- u32 reg, addr = bank->addresses->aint_en;
-
- reg = inl(TRID_REG(card, addr));
- reg |= mask;
- outl(reg, TRID_REG(card, addr));
-
-#ifdef DEBUG
- reg = inl(TRID_REG(card, addr));
- pr_debug("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n",
- channel, addr == T4D_AINTEN_B ? "AINTEN_B" : "AINTEN_A",
- reg, addr);
-#endif /* DEBUG */
-}
-
-static void
-trident_disable_voice_irq(struct trident_card *card, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
- struct trident_pcm_bank *bank = &card->banks[channel >> 5];
- u32 reg, addr = bank->addresses->aint_en;
-
- reg = inl(TRID_REG(card, addr));
- reg &= ~mask;
- outl(reg, TRID_REG(card, addr));
-
- /* Ack the channel in case the interrupt was set before we disable it. */
- outl(mask, TRID_REG(card, bank->addresses->aint));
-
-#ifdef DEBUG
- reg = inl(TRID_REG(card, addr));
- pr_debug("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n",
- channel, addr == T4D_AINTEN_B ? "AINTEN_B" : "AINTEN_A",
- reg, addr);
-#endif /* DEBUG */
-}
-
-static void
-trident_start_voice(struct trident_card *card, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
- struct trident_pcm_bank *bank = &card->banks[channel >> 5];
- u32 addr = bank->addresses->start;
-
-#ifdef DEBUG
- u32 reg;
-#endif /* DEBUG */
-
- outl(mask, TRID_REG(card, addr));
-
-#ifdef DEBUG
- reg = inl(TRID_REG(card, addr));
- pr_debug("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n",
- channel, addr == T4D_START_B ? "START_B" : "START_A",
- reg, addr);
-#endif /* DEBUG */
-}
-
-static void
-trident_stop_voice(struct trident_card *card, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
- struct trident_pcm_bank *bank = &card->banks[channel >> 5];
- u32 addr = bank->addresses->stop;
-
-#ifdef DEBUG
- u32 reg;
-#endif /* DEBUG */
-
- outl(mask, TRID_REG(card, addr));
-
-#ifdef DEBUG
- reg = inl(TRID_REG(card, addr));
- pr_debug("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n",
- channel, addr == T4D_STOP_B ? "STOP_B" : "STOP_A",
- reg, addr);
-#endif /* DEBUG */
-}
-
-static u32
-trident_get_interrupt_mask(struct trident_card *card, unsigned int channel)
-{
- struct trident_pcm_bank *bank = &card->banks[channel];
- u32 addr = bank->addresses->aint;
- return inl(TRID_REG(card, addr));
-}
-
-static int
-trident_check_channel_interrupt(struct trident_card *card, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
- u32 reg = trident_get_interrupt_mask(card, channel >> 5);
-
-#ifdef DEBUG
- if (reg & mask)
- pr_debug("trident: channel %d has interrupt, %s = 0x%08x\n",
- channel, reg == T4D_AINT_B ? "AINT_B" : "AINT_A",
- reg);
-#endif /* DEBUG */
- return (reg & mask) ? 1 : 0;
-}
-
-static void
-trident_ack_channel_interrupt(struct trident_card *card, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
- struct trident_pcm_bank *bank = &card->banks[channel >> 5];
- u32 reg, addr = bank->addresses->aint;
-
- reg = inl(TRID_REG(card, addr));
- reg &= mask;
- outl(reg, TRID_REG(card, addr));
-
-#ifdef DEBUG
- reg = inl(TRID_REG(card, T4D_AINT_B));
- pr_debug("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n",
- channel, reg);
-#endif /* DEBUG */
-}
-
-static struct trident_channel *
-trident_alloc_pcm_channel(struct trident_card *card)
-{
- struct trident_pcm_bank *bank;
- int idx;
-
- bank = &card->banks[BANK_B];
-
- for (idx = 31; idx >= 0; idx--) {
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx + 32;
- return channel;
- }
- }
-
- /* no more free channels available */
- printk(KERN_ERR "trident: no more channels available on Bank B.\n");
- return NULL;
-}
-
-static void
-trident_free_pcm_channel(struct trident_card *card, unsigned int channel)
-{
- int bank;
- unsigned char b;
-
- if (channel < 31 || channel > 63)
- return;
-
- if (card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_DX ||
- card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) {
- b = inb(TRID_REG(card, T4D_REC_CH));
- if ((b & ~0x80) == channel)
- outb(0x0, TRID_REG(card, T4D_REC_CH));
- }
-
- bank = channel >> 5;
- channel = channel & 0x1f;
-
- card->banks[bank].bitmap &= ~(1 << (channel));
-}
-
-static struct trident_channel *
-cyber_alloc_pcm_channel(struct trident_card *card)
-{
- struct trident_pcm_bank *bank;
- int idx;
-
- /* The cyberpro 5050 has only 32 voices and one bank */
- /* .. at least they are not documented (if you want to call that
- * crap documentation), perhaps broken ? */
-
- bank = &card->banks[BANK_A];
-
- for (idx = 31; idx >= 0; idx--) {
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx;
- return channel;
- }
- }
-
- /* no more free channels available */
- printk(KERN_ERR "cyberpro5050: no more channels available on Bank A.\n");
- return NULL;
-}
-
-static void
-cyber_free_pcm_channel(struct trident_card *card, unsigned int channel)
-{
- if (channel > 31)
- return;
- card->banks[BANK_A].bitmap &= ~(1 << (channel));
-}
-
-static inline void
-cyber_outidx(int port, int idx, int data)
-{
- outb(idx, port);
- outb(data, port + 1);
-}
-
-static inline int
-cyber_inidx(int port, int idx)
-{
- outb(idx, port);
- return inb(port + 1);
-}
-
-static int
-cyber_init_ritual(struct trident_card *card)
-{
- /* some black magic, taken from SDK samples */
- /* remove this and nothing will work */
- int portDat;
- int ret = 0;
- unsigned long flags;
-
- /*
- * Keep interrupts off for the configure - we don't want to
- * clash with another cyberpro config event
- */
-
- spin_lock_irqsave(&card->lock, flags);
- portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE);
- /* enable, if it was disabled */
- if ((portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE) {
- printk(KERN_INFO "cyberpro5050: enabling audio controller\n");
- cyber_outidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE,
- portDat | CYBER_BMSK_AUENZ_ENABLE);
- /* check again if hardware is enabled now */
- portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE);
- }
- if ((portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE) {
- printk(KERN_ERR "cyberpro5050: initAudioAccess: no success\n");
- ret = -1;
- } else {
- cyber_outidx(CYBER_PORT_AUDIO, CYBER_IDX_IRQ_ENABLE,
- CYBER_BMSK_AUDIO_INT_ENABLE);
- cyber_outidx(CYBER_PORT_AUDIO, 0xbf, 0x01);
- cyber_outidx(CYBER_PORT_AUDIO, 0xba, 0x20);
- cyber_outidx(CYBER_PORT_AUDIO, 0xbb, 0x08);
- cyber_outidx(CYBER_PORT_AUDIO, 0xbf, 0x02);
- cyber_outidx(CYBER_PORT_AUDIO, 0xb3, 0x06);
- cyber_outidx(CYBER_PORT_AUDIO, 0xbf, 0x00);
- }
- spin_unlock_irqrestore(&card->lock, flags);
- return ret;
-}
-
-/* called with spin lock held */
-
-static int
-trident_load_channel_registers(struct trident_card *card, u32 * data,
- unsigned int channel)
-{
- int i;
-
- if (channel > 63)
- return 0;
-
- /* select hardware channel to write */
- outb(channel, TRID_REG(card, T4D_LFO_GC_CIR));
-
- /* Output the channel registers, but don't write register
- three to an ALI chip. */
- for (i = 0; i < CHANNEL_REGS; i++) {
- if (i == 3 && card->pci_id == PCI_DEVICE_ID_ALI_5451)
- continue;
- outl(data[i], TRID_REG(card, CHANNEL_START + 4 * i));
- }
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451 ||
- card->pci_id == PCI_DEVICE_ID_INTERG_5050) {
- outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF1));
- outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF2));
- }
- return 1;
-}
-
-/* called with spin lock held */
-static int
-trident_write_voice_regs(struct trident_state *state)
-{
- unsigned int data[CHANNEL_REGS + 1];
- struct trident_channel *channel;
-
- channel = state->dmabuf.channel;
-
- data[1] = channel->lba;
- data[4] = channel->control;
-
- switch (state->card->pci_id) {
- case PCI_DEVICE_ID_ALI_5451:
- data[0] = 0; /* Current Sample Offset */
- data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
- data[3] = 0;
- break;
- case PCI_DEVICE_ID_SI_7018:
- case PCI_DEVICE_ID_INTERG_5050:
- data[0] = 0; /* Current Sample Offset */
- data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
- data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff);
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- data[0] = 0; /* Current Sample Offset */
- data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
- data[3] = channel->fm_vol & 0xffff;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- data[0] = (channel->delta << 24);
- data[2] = ((channel->delta << 16) & 0xff000000) |
- (channel->eso & 0x00ffffff);
- data[3] = channel->fm_vol & 0xffff;
- break;
- default:
- return 0;
- }
-
- return trident_load_channel_registers(state->card, data, channel->num);
-}
-
-static int
-compute_rate_play(u32 rate)
-{
- int delta;
- /* 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) + rate) / 48000) & 0x0000ffff;
- return delta;
-}
-
-static int
-compute_rate_rec(u32 rate)
-{
- int delta;
-
- if (rate == 44100)
- delta = 0x116a;
- else if (rate == 8000)
- delta = 0x6000;
- else if (rate == 48000)
- delta = 0x1000;
- else
- delta = ((48000 << 12) / rate) & 0x0000ffff;
-
- return delta;
-}
-
-/* set playback sample rate */
-static unsigned int
-trident_set_dac_rate(struct trident_state *state, unsigned int rate)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
-
- if (rate > 48000)
- rate = 48000;
- if (rate < 4000)
- rate = 4000;
-
- dmabuf->rate = rate;
- dmabuf->channel->delta = compute_rate_play(rate);
-
- trident_write_voice_regs(state);
-
- pr_debug("trident: called trident_set_dac_rate : rate = %d\n", rate);
-
- return rate;
-}
-
-/* set recording sample rate */
-static unsigned int
-trident_set_adc_rate(struct trident_state *state, unsigned int rate)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
-
- if (rate > 48000)
- rate = 48000;
- if (rate < 4000)
- rate = 4000;
-
- dmabuf->rate = rate;
- dmabuf->channel->delta = compute_rate_rec(rate);
-
- trident_write_voice_regs(state);
-
- pr_debug("trident: called trident_set_adc_rate : rate = %d\n", rate);
-
- return rate;
-}
-
-/* prepare channel attributes for playback */
-static void
-trident_play_setup(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- struct trident_channel *channel = dmabuf->channel;
-
- channel->lba = dmabuf->dma_handle;
- channel->delta = compute_rate_play(dmabuf->rate);
-
- channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
- channel->eso -= 1;
-
- if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
- channel->attribute = 0;
- if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- if ((channel->num == ALI_SPDIF_IN_CHANNEL) ||
- (channel->num == ALI_PCM_IN_CHANNEL))
- ali_disable_special_channel(state->card, channel->num);
- else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL))
- & ALI_SPDIF_OUT_CH_ENABLE)
- && (channel->num == ALI_SPDIF_OUT_CHANNEL)) {
- ali_set_spdif_out_rate(state->card,
- state->dmabuf.rate);
- state->dmabuf.channel->delta = 0x1000;
- }
- }
- }
-
- channel->fm_vol = 0x0;
-
- channel->control = CHANNEL_LOOP;
- if (dmabuf->fmt & TRIDENT_FMT_16BIT) {
- /* 16-bits */
- channel->control |= CHANNEL_16BITS;
- /* signed */
- channel->control |= CHANNEL_SIGNED;
- }
- if (dmabuf->fmt & TRIDENT_FMT_STEREO)
- /* stereo */
- channel->control |= CHANNEL_STEREO;
-
- pr_debug("trident: trident_play_setup, LBA = 0x%08x, Delta = 0x%08x, "
- "ESO = 0x%08x, Control = 0x%08x\n", channel->lba,
- channel->delta, channel->eso, channel->control);
-
- trident_write_voice_regs(state);
-}
-
-/* prepare channel attributes for recording */
-static void
-trident_rec_setup(struct trident_state *state)
-{
- u16 w;
- u8 bval;
-
- struct trident_card *card = state->card;
- struct dmabuf *dmabuf = &state->dmabuf;
- struct trident_channel *channel = dmabuf->channel;
- unsigned int rate;
-
- /* Enable AC-97 ADC (capture) */
- switch (card->pci_id) {
- case PCI_DEVICE_ID_ALI_5451:
- ali_enable_special_channel(state);
- break;
- case PCI_DEVICE_ID_SI_7018:
- /* for 7018, the ac97 is always in playback/record (duplex) mode */
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
- outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
- /* enable and set record channel */
- outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- w = inw(TRID_REG(card, T4D_MISCINT));
- outw(w | 0x1000, TRID_REG(card, T4D_MISCINT));
- /* enable and set record channel */
- outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
- break;
- case PCI_DEVICE_ID_INTERG_5050:
- /* don't know yet, using special channel 22 in GC1(0xd4)? */
- break;
- default:
- return;
- }
-
- channel->lba = dmabuf->dma_handle;
- channel->delta = compute_rate_rec(dmabuf->rate);
- if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) &&
- (channel->num == ALI_SPDIF_IN_CHANNEL)) {
- rate = ali_get_spdif_in_rate(card);
- if (rate == 0) {
- printk(KERN_WARNING "trident: ALi 5451 "
- "S/PDIF input setup error!\n");
- rate = 48000;
- }
- bval = inb(TRID_REG(card, ALI_SPDIF_CTRL));
- if (bval & 0x10) {
- outb(bval, TRID_REG(card, ALI_SPDIF_CTRL));
- printk(KERN_WARNING "trident: cleared ALi "
- "5451 S/PDIF parity error flag.\n");
- }
-
- if (rate != 48000)
- channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff;
- }
-
- channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
- channel->eso -= 1;
-
- if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
- channel->attribute = 0;
- }
-
- channel->fm_vol = 0x0;
-
- channel->control = CHANNEL_LOOP;
- if (dmabuf->fmt & TRIDENT_FMT_16BIT) {
- /* 16-bits */
- channel->control |= CHANNEL_16BITS;
- /* signed */
- channel->control |= CHANNEL_SIGNED;
- }
- if (dmabuf->fmt & TRIDENT_FMT_STEREO)
- /* stereo */
- channel->control |= CHANNEL_STEREO;
-
- pr_debug("trident: trident_rec_setup, LBA = 0x%08x, Delat = 0x%08x, "
- "ESO = 0x%08x, Control = 0x%08x\n", channel->lba,
- channel->delta, channel->eso, channel->control);
-
- trident_write_voice_regs(state);
-}
-
-/* get current playback/recording dma buffer pointer (byte offset from LBA),
- called with spinlock held! */
-static inline unsigned
-trident_get_dma_addr(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- u32 cso;
-
- if (!dmabuf->enable)
- return 0;
-
- outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR));
-
- switch (state->card->pci_id) {
- case PCI_DEVICE_ID_ALI_5451:
- case PCI_DEVICE_ID_SI_7018:
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- case PCI_DEVICE_ID_INTERG_5050:
- /* 16 bits ESO, CSO for 7018 and DX */
- cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- /* 24 bits ESO, CSO for NX */
- cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
- break;
- default:
- return 0;
- }
-
- pr_debug("trident: trident_get_dma_addr: chip reported channel: %d, "
- "cso = 0x%04x\n", dmabuf->channel->num, cso);
-
- /* ESO and CSO are in units of Samples, convert to byte offset */
- cso <<= sample_shift[dmabuf->fmt];
-
- return (cso % dmabuf->dmasize);
-}
-
-/* Stop recording (lock held) */
-static inline void
-__stop_adc(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned int chan_num = dmabuf->channel->num;
- struct trident_card *card = state->card;
-
- dmabuf->enable &= ~ADC_RUNNING;
- trident_stop_voice(card, chan_num);
- trident_disable_voice_irq(card, chan_num);
-}
-
-static void
-stop_adc(struct trident_state *state)
-{
- struct trident_card *card = state->card;
- unsigned long flags;
-
- spin_lock_irqsave(&card->lock, flags);
- __stop_adc(state);
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-static void
-start_adc(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned int chan_num = dmabuf->channel->num;
- struct trident_card *card = state->card;
- unsigned long flags;
-
- spin_lock_irqsave(&card->lock, flags);
- if ((dmabuf->mapped ||
- dmabuf->count < (signed) dmabuf->dmasize) &&
- dmabuf->ready) {
- dmabuf->enable |= ADC_RUNNING;
- trident_enable_voice_irq(card, chan_num);
- trident_start_voice(card, chan_num);
- }
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-/* stop playback (lock held) */
-static inline void
-__stop_dac(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned int chan_num = dmabuf->channel->num;
- struct trident_card *card = state->card;
-
- dmabuf->enable &= ~DAC_RUNNING;
- trident_stop_voice(card, chan_num);
- if (state->chans_num == 6) {
- trident_stop_voice(card, state->other_states[0]->
- dmabuf.channel->num);
- trident_stop_voice(card, state->other_states[1]->
- dmabuf.channel->num);
- trident_stop_voice(card, state->other_states[2]->
- dmabuf.channel->num);
- trident_stop_voice(card, state->other_states[3]->
- dmabuf.channel->num);
- }
- trident_disable_voice_irq(card, chan_num);
-}
-
-static void
-stop_dac(struct trident_state *state)
-{
- struct trident_card *card = state->card;
- unsigned long flags;
-
- spin_lock_irqsave(&card->lock, flags);
- __stop_dac(state);
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-static void
-start_dac(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned int chan_num = dmabuf->channel->num;
- struct trident_card *card = state->card;
- unsigned long flags;
-
- spin_lock_irqsave(&card->lock, flags);
- if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
- dmabuf->enable |= DAC_RUNNING;
- trident_enable_voice_irq(card, chan_num);
- trident_start_voice(card, chan_num);
- if (state->chans_num == 6) {
- trident_start_voice(card, state->other_states[0]->
- dmabuf.channel->num);
- trident_start_voice(card, state->other_states[1]->
- dmabuf.channel->num);
- trident_start_voice(card, state->other_states[2]->
- dmabuf.channel->num);
- trident_start_voice(card, state->other_states[3]->
- dmabuf.channel->num);
- }
- }
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
-#define DMABUF_MINORDER 1
-
-/* alloc a DMA buffer of with a buffer of this order */
-static int
-alloc_dmabuf(struct dmabuf *dmabuf, struct pci_dev *pci_dev, int order)
-{
- void *rawbuf = NULL;
- struct page *page, *pend;
-
- if (!(rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order,
- &dmabuf->dma_handle)))
- return -ENOMEM;
-
- pr_debug("trident: allocated %ld (order = %d) bytes at %p\n",
- PAGE_SIZE << order, order, rawbuf);
-
- dmabuf->ready = dmabuf->mapped = 0;
- dmabuf->rawbuf = rawbuf;
- dmabuf->buforder = order;
-
- /* now mark the pages as reserved; otherwise */
- /* remap_pfn_range doesn't do what we want */
- pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
- for (page = virt_to_page(rawbuf); page <= pend; page++)
- SetPageReserved(page);
-
- return 0;
-}
-
-/* allocate the main DMA buffer, playback and recording buffer should be */
-/* allocated separately */
-static int
-alloc_main_dmabuf(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- int order;
- int ret = -ENOMEM;
-
- /* alloc as big a chunk as we can, FIXME: is this necessary ?? */
- for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
- if (!(ret = alloc_dmabuf(dmabuf, state->card->pci_dev, order)))
- return 0;
- /* else try again */
- }
- return ret;
-}
-
-/* deallocate a DMA buffer */
-static void
-dealloc_dmabuf(struct dmabuf *dmabuf, struct pci_dev *pci_dev)
-{
- struct page *page, *pend;
-
- if (dmabuf->rawbuf) {
- /* undo marking the pages as reserved */
- pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
- for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
- ClearPageReserved(page);
- pci_free_consistent(pci_dev, PAGE_SIZE << dmabuf->buforder,
- dmabuf->rawbuf, dmabuf->dma_handle);
- dmabuf->rawbuf = NULL;
- }
- dmabuf->mapped = dmabuf->ready = 0;
-}
-
-static int
-prog_dmabuf(struct trident_state *state, enum dmabuf_mode rec)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned bytepersec;
- struct trident_state *s = state;
- unsigned bufsize, dma_nums;
- unsigned long flags;
- int ret, i, order;
-
- if ((ret = lock_set_fmt(state)) < 0)
- return ret;
-
- if (state->chans_num == 6)
- dma_nums = 5;
- else
- dma_nums = 1;
-
- for (i = 0; i < dma_nums; i++) {
- if (i > 0) {
- s = state->other_states[i - 1];
- dmabuf = &s->dmabuf;
- dmabuf->fmt = state->dmabuf.fmt;
- dmabuf->rate = state->dmabuf.rate;
- }
-
- spin_lock_irqsave(&s->card->lock, flags);
- dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
- dmabuf->count = dmabuf->error = 0;
- spin_unlock_irqrestore(&s->card->lock, flags);
-
- /* allocate DMA buffer if not allocated yet */
- if (!dmabuf->rawbuf) {
- if (i == 0) {
- if ((ret = alloc_main_dmabuf(state))) {
- unlock_set_fmt(state);
- return ret;
- }
- } else {
- ret = -ENOMEM;
- order = state->dmabuf.buforder - 1;
- if (order >= DMABUF_MINORDER) {
- ret = alloc_dmabuf(dmabuf,
- state->card->pci_dev,
- order);
- }
- if (ret) {
- /* release the main DMA buffer */
- dealloc_dmabuf(&state->dmabuf, state->card->pci_dev);
- /* release the auxiliary DMA buffers */
- for (i -= 2; i >= 0; i--)
- dealloc_dmabuf(&state->other_states[i]->dmabuf,
- state->card->pci_dev);
- unlock_set_fmt(state);
- return ret;
- }
- }
- }
- /* FIXME: figure out all this OSS fragment stuff */
- bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
- bufsize = PAGE_SIZE << dmabuf->buforder;
- if (dmabuf->ossfragshift) {
- if ((1000 << dmabuf->ossfragshift) < bytepersec)
- dmabuf->fragshift = ld2(bytepersec / 1000);
- else
- dmabuf->fragshift = dmabuf->ossfragshift;
- } else {
- /* lets hand out reasonable big ass buffers by default */
- dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2);
- }
- dmabuf->numfrag = bufsize >> dmabuf->fragshift;
- while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
- dmabuf->fragshift--;
- dmabuf->numfrag = bufsize >> dmabuf->fragshift;
- }
- dmabuf->fragsize = 1 << dmabuf->fragshift;
- if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
- dmabuf->numfrag = dmabuf->ossmaxfrags;
- dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
- dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
-
- memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80,
- dmabuf->dmasize);
-
- spin_lock_irqsave(&s->card->lock, flags);
- if (rec == DM_RECORD)
- trident_rec_setup(s);
- else /* DM_PLAYBACK */
- trident_play_setup(s);
-
- spin_unlock_irqrestore(&s->card->lock, flags);
-
- /* set the ready flag for the dma buffer */
- dmabuf->ready = 1;
-
- pr_debug("trident: prog_dmabuf(%d), sample rate = %d, "
- "format = %d, numfrag = %d, fragsize = %d "
- "dmasize = %d\n", dmabuf->channel->num,
- dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
- dmabuf->fragsize, dmabuf->dmasize);
- }
- unlock_set_fmt(state);
- return 0;
-}
-
-
-static inline int prog_dmabuf_record(struct trident_state* state)
-{
- return prog_dmabuf(state, DM_RECORD);
-}
-
-static inline int prog_dmabuf_playback(struct trident_state* state)
-{
- return prog_dmabuf(state, DM_PLAYBACK);
-}
-
-/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.
- |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx|
- but we almost always get this
- |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------|
- so we have to clear the tail space to "silence"
- |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000|
-*/
-static void
-trident_clear_tail(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned swptr;
- unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80;
- unsigned int len;
- unsigned long flags;
-
- spin_lock_irqsave(&state->card->lock, flags);
- swptr = dmabuf->swptr;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- if (swptr == 0 || swptr == dmabuf->dmasize / 2 ||
- swptr == dmabuf->dmasize)
- return;
-
- if (swptr < dmabuf->dmasize / 2)
- len = dmabuf->dmasize / 2 - swptr;
- else
- len = dmabuf->dmasize - swptr;
-
- memset(dmabuf->rawbuf + swptr, silence, len);
- if (state->card->pci_id != PCI_DEVICE_ID_ALI_5451) {
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->swptr += len;
- dmabuf->count += len;
- spin_unlock_irqrestore(&state->card->lock, flags);
- }
-
- /* restart the dma machine in case it is halted */
- start_dac(state);
-}
-
-static int
-drain_dac(struct trident_state *state, int nonblock)
-{
- DECLARE_WAITQUEUE(wait, current);
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned long flags;
- unsigned long tmo;
- int count;
- unsigned long diff = 0;
-
- if (dmabuf->mapped || !dmabuf->ready)
- return 0;
-
- add_wait_queue(&dmabuf->wait, &wait);
- for (;;) {
- /* It seems that we have to set the current state to TASK_INTERRUPTIBLE
- every time to make the process really go to sleep */
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&state->card->lock, flags);
- count = dmabuf->count;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- if (count <= 0)
- break;
-
- if (signal_pending(current))
- break;
-
- if (nonblock) {
- remove_wait_queue(&dmabuf->wait, &wait);
- set_current_state(TASK_RUNNING);
- return -EBUSY;
- }
-
- /* No matter how much data is left in the buffer, we have to wait until
- CSO == ESO/2 or CSO == ESO when address engine interrupts */
- if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451 ||
- state->card->pci_id == PCI_DEVICE_ID_INTERG_5050) {
- diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize;
- diff = diff % (dmabuf->dmasize);
- tmo = (diff * HZ) / dmabuf->rate;
- } else {
- tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
- }
- tmo >>= sample_shift[dmabuf->fmt];
- if (!schedule_timeout(tmo ? tmo : 1) && tmo) {
- break;
- }
- }
- remove_wait_queue(&dmabuf->wait, &wait);
- set_current_state(TASK_RUNNING);
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- return 0;
-}
-
-/* update buffer manangement pointers, especially, */
-/* dmabuf->count and dmabuf->hwptr */
-static void
-trident_update_ptr(struct trident_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned hwptr, swptr;
- int clear_cnt = 0;
- int diff;
- unsigned char silence;
- unsigned half_dmasize;
-
- /* update hardware pointer */
- hwptr = trident_get_dma_addr(state);
- diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
- dmabuf->hwptr = hwptr;
- dmabuf->total_bytes += diff;
-
- /* error handling and process wake up for ADC */
- if (dmabuf->enable == ADC_RUNNING) {
- if (dmabuf->mapped) {
- dmabuf->count -= diff;
- if (dmabuf->count >= (signed) dmabuf->fragsize)
- wake_up(&dmabuf->wait);
- } else {
- dmabuf->count += diff;
-
- if (dmabuf->count < 0 ||
- dmabuf->count > dmabuf->dmasize) {
- /* buffer underrun or buffer overrun, */
- /* we have no way to recover it here, just */
- /* stop the machine and let the process */
- /* force hwptr and swptr to sync */
- __stop_adc(state);
- dmabuf->error++;
- }
- if (dmabuf->count < (signed) dmabuf->dmasize / 2)
- wake_up(&dmabuf->wait);
- }
- }
-
- /* error handling and process wake up for DAC */
- if (dmabuf->enable == DAC_RUNNING) {
- if (dmabuf->mapped) {
- dmabuf->count += diff;
- if (dmabuf->count >= (signed) dmabuf->fragsize)
- wake_up(&dmabuf->wait);
- } else {
- dmabuf->count -= diff;
-
- if (dmabuf->count < 0 ||
- dmabuf->count > dmabuf->dmasize) {
- /* buffer underrun or buffer overrun, we have no way to recover
- it here, just stop the machine and let the process force hwptr
- and swptr to sync */
- __stop_dac(state);
- dmabuf->error++;
- } else if (!dmabuf->endcleared) {
- swptr = dmabuf->swptr;
- silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80);
- if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) {
- /* We must clear end data of 1/2 dmabuf if needed.
- According to 1/2 algorithm of Address Engine Interrupt,
- check the validation of the data of half dmasize. */
- half_dmasize = dmabuf->dmasize / 2;
- if ((diff = hwptr - half_dmasize) < 0)
- diff = hwptr;
- if ((dmabuf->count + diff) < half_dmasize) {
- //there is invalid data in the end of half buffer
- if ((clear_cnt = half_dmasize - swptr) < 0)
- clear_cnt += half_dmasize;
- //clear the invalid data
- memset(dmabuf->rawbuf + swptr, silence, clear_cnt);
- if (state->chans_num == 6) {
- clear_cnt = clear_cnt / 2;
- swptr = swptr / 2;
- memset(state->other_states[0]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- memset(state->other_states[1]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- memset(state->other_states[2]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- memset(state->other_states[3]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- }
- dmabuf->endcleared = 1;
- }
- } else if (dmabuf->count < (signed) dmabuf->fragsize) {
- clear_cnt = dmabuf->fragsize;
- if ((swptr + clear_cnt) > dmabuf->dmasize)
- clear_cnt = dmabuf->dmasize - swptr;
- memset(dmabuf->rawbuf + swptr, silence, clear_cnt);
- if (state->chans_num == 6) {
- clear_cnt = clear_cnt / 2;
- swptr = swptr / 2;
- memset(state->other_states[0]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- memset(state->other_states[1]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- memset(state->other_states[2]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- memset(state->other_states[3]->dmabuf.rawbuf + swptr,
- silence, clear_cnt);
- }
- dmabuf->endcleared = 1;
- }
- }
- /* trident_update_ptr is called by interrupt handler or by process via
- ioctl/poll, we only wake up the waiting process when we have more
- than 1/2 buffer free (always true for interrupt handler) */
- if (dmabuf->count < (signed) dmabuf->dmasize / 2)
- wake_up(&dmabuf->wait);
- }
- }
- dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE;
-}
-
-static void
-trident_address_interrupt(struct trident_card *card)
-{
- int i;
- struct trident_state *state;
- unsigned int channel;
-
- /* Update the pointers for all channels we are running. */
- /* FIXME: should read interrupt status only once */
- for (i = 0; i < NR_HW_CH; i++) {
- channel = 63 - i;
- if (trident_check_channel_interrupt(card, channel)) {
- trident_ack_channel_interrupt(card, channel);
- if ((state = card->states[i]) != NULL) {
- trident_update_ptr(state);
- } else {
- printk(KERN_WARNING "trident: spurious channel "
- "irq %d.\n", channel);
- trident_stop_voice(card, channel);
- trident_disable_voice_irq(card, channel);
- }
- }
- }
-}
-
-static void
-ali_hwvol_control(struct trident_card *card, int opt)
-{
- u16 dwTemp, volume[2], mute, diff, *pVol[2];
-
- dwTemp = ali_ac97_read(card->ac97_codec[0], 0x02);
- mute = dwTemp & 0x8000;
- volume[0] = dwTemp & 0x001f;
- volume[1] = (dwTemp & 0x1f00) >> 8;
- if (volume[0] < volume[1]) {
- pVol[0] = &volume[0];
- pVol[1] = &volume[1];
- } else {
- pVol[1] = &volume[0];
- pVol[0] = &volume[1];
- }
- diff = *(pVol[1]) - *(pVol[0]);
-
- if (opt == 1) { // MUTE
- dwTemp ^= 0x8000;
- ali_ac97_write(card->ac97_codec[0],
- 0x02, dwTemp);
- } else if (opt == 2) { // Down
- if (mute)
- return;
- if (*(pVol[1]) < 0x001f) {
- (*pVol[1])++;
- *(pVol[0]) = *(pVol[1]) - diff;
- }
- dwTemp &= 0xe0e0;
- dwTemp |= (volume[0]) | (volume[1] << 8);
- ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);
- card->ac97_codec[0]->mixer_state[0] = ((32 - volume[0]) * 25 / 8) |
- (((32 - volume[1]) * 25 / 8) << 8);
- } else if (opt == 4) { // Up
- if (mute)
- return;
- if (*(pVol[0]) > 0) {
- (*pVol[0])--;
- *(pVol[1]) = *(pVol[0]) + diff;
- }
- dwTemp &= 0xe0e0;
- dwTemp |= (volume[0]) | (volume[1] << 8);
- ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);
- card->ac97_codec[0]->mixer_state[0] = ((32 - volume[0]) * 25 / 8) |
- (((32 - volume[1]) * 25 / 8) << 8);
- } else {
- /* Nothing needs doing */
- }
-}
-
-/*
- * Re-enable reporting of vol change after 0.1 seconds
- */
-
-static void
-ali_timeout(unsigned long ptr)
-{
- struct trident_card *card = (struct trident_card *) ptr;
- u16 temp = 0;
-
- /* Enable GPIO IRQ (MISCINT bit 18h) */
- temp = inw(TRID_REG(card, T4D_MISCINT + 2));
- temp |= 0x0004;
- outw(temp, TRID_REG(card, T4D_MISCINT + 2));
-}
-
-/*
- * Set up the timer to clear the vol change notification
- */
-
-static void
-ali_set_timer(struct trident_card *card)
-{
- /* Add Timer Routine to Enable GPIO IRQ */
- del_timer(&card->timer); /* Never queue twice */
- card->timer.function = ali_timeout;
- card->timer.data = (unsigned long) card;
- card->timer.expires = jiffies + HZ / 10;
- add_timer(&card->timer);
-}
-
-/*
- * Process a GPIO event
- */
-
-static void
-ali_queue_task(struct trident_card *card, int opt)
-{
- u16 temp;
-
- /* Disable GPIO IRQ (MISCINT bit 18h) */
- temp = inw(TRID_REG(card, T4D_MISCINT + 2));
- temp &= (u16) (~0x0004);
- outw(temp, TRID_REG(card, T4D_MISCINT + 2));
-
- /* Adjust the volume */
- ali_hwvol_control(card, opt);
-
- /* Set the timer for 1/10th sec */
- ali_set_timer(card);
-}
-
-static void
-cyber_address_interrupt(struct trident_card *card)
-{
- int i, irq_status;
- struct trident_state *state;
- unsigned int channel;
-
- /* Update the pointers for all channels we are running. */
- /* FIXED: read interrupt status only once */
- irq_status = inl(TRID_REG(card, T4D_AINT_A));
-
- pr_debug("cyber_address_interrupt: irq_status 0x%X\n", irq_status);
-
- for (i = 0; i < NR_HW_CH; i++) {
- channel = 31 - i;
- if (irq_status & (1 << channel)) {
- /* clear bit by writing a 1, zeroes are ignored */
- outl((1 << channel), TRID_REG(card, T4D_AINT_A));
-
- pr_debug("cyber_interrupt: channel %d\n", channel);
-
- if ((state = card->states[i]) != NULL) {
- trident_update_ptr(state);
- } else {
- printk(KERN_WARNING "cyber5050: spurious "
- "channel irq %d.\n", channel);
- trident_stop_voice(card, channel);
- trident_disable_voice_irq(card, channel);
- }
- }
- }
-}
-
-static irqreturn_t
-trident_interrupt(int irq, void *dev_id)
-{
- struct trident_card *card = (struct trident_card *) dev_id;
- u32 event;
- u32 gpio;
-
- spin_lock(&card->lock);
- event = inl(TRID_REG(card, T4D_MISCINT));
-
- pr_debug("trident: trident_interrupt called, MISCINT = 0x%08x\n",
- event);
-
- if (event & ADDRESS_IRQ) {
- card->address_interrupt(card);
- }
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- /* GPIO IRQ (H/W Volume Control) */
- event = inl(TRID_REG(card, T4D_MISCINT));
- if (event & (1 << 25)) {
- gpio = inl(TRID_REG(card, ALI_GPIO));
- if (!timer_pending(&card->timer))
- ali_queue_task(card, gpio & 0x07);
- }
- event = inl(TRID_REG(card, T4D_MISCINT));
- outl(event | (ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
- TRID_REG(card, T4D_MISCINT));
- spin_unlock(&card->lock);
- return IRQ_HANDLED;
- }
-
- /* manually clear interrupt status, bad hardware design, blame T^2 */
- outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
- TRID_REG(card, T4D_MISCINT));
- spin_unlock(&card->lock);
- return IRQ_HANDLED;
-}
-
-/* in this loop, dmabuf.count signifies the amount of data that is waiting */
-/* to be copied to the user's buffer. it is filled by the dma machine and */
-/* drained by this loop. */
-static ssize_t
-trident_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
-{
- struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dmabuf;
- ssize_t ret = 0;
- unsigned long flags;
- unsigned swptr;
- int cnt;
-
- pr_debug("trident: trident_read called, count = %zd\n", count);
-
- VALIDATE_STATE(state);
-
- if (dmabuf->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
-
- mutex_lock(&state->sem);
- if (!dmabuf->ready && (ret = prog_dmabuf_record(state)))
- goto out;
-
- while (count > 0) {
- spin_lock_irqsave(&state->card->lock, flags);
- if (dmabuf->count > (signed) dmabuf->dmasize) {
- /* buffer overrun, we are recovering from */
- /* sleep_on_timeout, resync hwptr and swptr, */
- /* make process flush the buffer */
- dmabuf->count = dmabuf->dmasize;
- dmabuf->swptr = dmabuf->hwptr;
- }
- swptr = dmabuf->swptr;
- cnt = dmabuf->dmasize - swptr;
- if (dmabuf->count < cnt)
- cnt = dmabuf->count;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- if (cnt > count)
- cnt = count;
- if (cnt <= 0) {
- unsigned long tmo;
- /* buffer is empty, start the dma machine and */
- /* wait for data to be recorded */
- start_adc(state);
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
-
- mutex_unlock(&state->sem);
- /* No matter how much space left in the buffer, */
- /* we have to wait until CSO == ESO/2 or CSO == ESO */
- /* when address engine interrupts */
- tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
- /* There are two situations when sleep_on_timeout returns, one is when
- the interrupt is serviced correctly and the process is waked up by
- ISR ON TIME. Another is when timeout is expired, which means that
- either interrupt is NOT serviced correctly (pending interrupt) or it
- is TOO LATE for the process to be scheduled to run (scheduler latency)
- which results in a (potential) buffer overrun. And worse, there is
- NOTHING we can do to prevent it. */
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
- pr_debug(KERN_ERR "trident: recording schedule timeout, "
- "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
- dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
- dmabuf->hwptr, dmabuf->swptr);
-
- /* a buffer overrun, we delay the recovery until next time the
- while loop begin and we REALLY have space to record */
- }
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out;
- }
- mutex_lock(&state->sem);
- if (dmabuf->mapped) {
- if (!ret)
- ret = -ENXIO;
- goto out;
- }
- continue;
- }
-
- if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- swptr = (swptr + cnt) % dmabuf->dmasize;
-
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->swptr = swptr;
- dmabuf->count -= cnt;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- count -= cnt;
- buffer += cnt;
- ret += cnt;
- start_adc(state);
- }
-out:
- mutex_unlock(&state->sem);
- return ret;
-}
-
-/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
- the soundcard. it is drained by the dma machine and filled by this loop. */
-
-static ssize_t
-trident_write(struct file *file, const char __user *buffer, size_t count, loff_t * ppos)
-{
- struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dmabuf;
- ssize_t ret;
- unsigned long flags;
- unsigned swptr;
- int cnt;
- unsigned int state_cnt;
- unsigned int copy_count;
- int lret; /* for lock_set_fmt */
-
- pr_debug("trident: trident_write called, count = %zd\n", count);
-
- VALIDATE_STATE(state);
-
- /*
- * Guard against an mmap or ioctl while writing
- */
-
- mutex_lock(&state->sem);
-
- if (dmabuf->mapped) {
- ret = -ENXIO;
- goto out;
- }
- if (!dmabuf->ready && (ret = prog_dmabuf_playback(state)))
- goto out;
-
- if (!access_ok(VERIFY_READ, buffer, count)) {
- ret = -EFAULT;
- goto out;
- }
-
- ret = 0;
-
- while (count > 0) {
- spin_lock_irqsave(&state->card->lock, flags);
- if (dmabuf->count < 0) {
- /* buffer underrun, we are recovering from */
- /* sleep_on_timeout, resync hwptr and swptr */
- dmabuf->count = 0;
- dmabuf->swptr = dmabuf->hwptr;
- }
- swptr = dmabuf->swptr;
- cnt = dmabuf->dmasize - swptr;
- if (dmabuf->count + cnt > dmabuf->dmasize)
- cnt = dmabuf->dmasize - dmabuf->count;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- if (cnt > count)
- cnt = count;
- if (cnt <= 0) {
- unsigned long tmo;
- /* buffer is full, start the dma machine and */
- /* wait for data to be played */
- start_dac(state);
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- /* No matter how much data left in the buffer, */
- /* we have to wait until CSO == ESO/2 or CSO == ESO */
- /* when address engine interrupts */
- lock_set_fmt(state);
- tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
- unlock_set_fmt(state);
- mutex_unlock(&state->sem);
-
- /* There are two situations when sleep_on_timeout */
- /* returns, one is when the interrupt is serviced */
- /* correctly and the process is waked up by ISR */
- /* ON TIME. Another is when timeout is expired, which */
- /* means that either interrupt is NOT serviced */
- /* correctly (pending interrupt) or it is TOO LATE */
- /* for the process to be scheduled to run */
- /* (scheduler latency) which results in a (potential) */
- /* buffer underrun. And worse, there is NOTHING we */
- /* can do to prevent it. */
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
- pr_debug(KERN_ERR "trident: playback schedule "
- "timeout, dmasz %u fragsz %u count %i "
- "hwptr %u swptr %u\n", dmabuf->dmasize,
- dmabuf->fragsize, dmabuf->count,
- dmabuf->hwptr, dmabuf->swptr);
-
- /* a buffer underrun, we delay the recovery */
- /* until next time the while loop begin and */
- /* we REALLY have data to play */
- }
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out_nolock;
- }
- mutex_lock(&state->sem);
- if (dmabuf->mapped) {
- if (!ret)
- ret = -ENXIO;
- goto out;
- }
- continue;
- }
- if ((lret = lock_set_fmt(state)) < 0) {
- ret = lret;
- goto out;
- }
-
- if (state->chans_num == 6) {
- copy_count = 0;
- state_cnt = 0;
- if (ali_write_5_1(state, buffer, cnt, &copy_count,
- &state_cnt) == -EFAULT) {
- if (state_cnt) {
- swptr = (swptr + state_cnt) % dmabuf->dmasize;
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->swptr = swptr;
- dmabuf->count += state_cnt;
- dmabuf->endcleared = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
- }
- ret += copy_count;
- if (!ret)
- ret = -EFAULT;
- unlock_set_fmt(state);
- goto out;
- }
- } else {
- if (copy_from_user(dmabuf->rawbuf + swptr,
- buffer, cnt)) {
- if (!ret)
- ret = -EFAULT;
- unlock_set_fmt(state);
- goto out;
- }
- state_cnt = cnt;
- }
- unlock_set_fmt(state);
-
- swptr = (swptr + state_cnt) % dmabuf->dmasize;
-
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->swptr = swptr;
- dmabuf->count += state_cnt;
- dmabuf->endcleared = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- count -= cnt;
- buffer += cnt;
- ret += cnt;
- start_dac(state);
- }
-out:
- mutex_unlock(&state->sem);
-out_nolock:
- return ret;
-}
-
-/* No kernel lock - we have our own spinlock */
-static unsigned int
-trident_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned long flags;
- unsigned int mask = 0;
-
- VALIDATE_STATE(state);
-
- /*
- * Guard against a parallel poll and write causing multiple
- * prog_dmabuf events
- */
-
- mutex_lock(&state->sem);
-
- if (file->f_mode & FMODE_WRITE) {
- if (!dmabuf->ready && prog_dmabuf_playback(state)) {
- mutex_unlock(&state->sem);
- return 0;
- }
- poll_wait(file, &dmabuf->wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!dmabuf->ready && prog_dmabuf_record(state)) {
- mutex_unlock(&state->sem);
- return 0;
- }
- poll_wait(file, &dmabuf->wait, wait);
- }
-
- mutex_unlock(&state->sem);
-
- spin_lock_irqsave(&state->card->lock, flags);
- trident_update_ptr(state);
- if (file->f_mode & FMODE_READ) {
- if (dmabuf->count >= (signed) dmabuf->fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (dmabuf->mapped) {
- if (dmabuf->count >= (signed) dmabuf->fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed) dmabuf->dmasize >= dmabuf->count +
- (signed) dmabuf->fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- return mask;
-}
-
-static int
-trident_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dmabuf;
- int ret = -EINVAL;
- unsigned long size;
-
- VALIDATE_STATE(state);
-
- /*
- * Lock against poll read write or mmap creating buffers. Also lock
- * a read or write against an mmap.
- */
-
- mutex_lock(&state->sem);
-
- if (vma->vm_flags & VM_WRITE) {
- if ((ret = prog_dmabuf_playback(state)) != 0)
- goto out;
- } else if (vma->vm_flags & VM_READ) {
- if ((ret = prog_dmabuf_record(state)) != 0)
- goto out;
- } else
- goto out;
-
- ret = -EINVAL;
- if (vma->vm_pgoff != 0)
- goto out;
- size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << dmabuf->buforder))
- goto out;
- ret = -EAGAIN;
- if (remap_pfn_range(vma, vma->vm_start,
- virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT,
- size, vma->vm_page_prot))
- goto out;
- dmabuf->mapped = 1;
- ret = 0;
-out:
- mutex_unlock(&state->sem);
- return ret;
-}
-
-static int
-trident_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int val, mapped, ret = 0;
- struct trident_card *card = state->card;
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
-
- VALIDATE_STATE(state);
-
-
- mapped = ((file->f_mode & (FMODE_WRITE | FMODE_READ)) && dmabuf->mapped);
-
- pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
- _IOC_NR(cmd), arg ? *p : 0);
-
- switch (cmd) {
- case OSS_GETVERSION:
- ret = put_user(SOUND_VERSION, p);
- break;
-
- case SNDCTL_DSP_RESET:
- /* FIXME: spin_lock ? */
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- synchronize_irq(card->irq);
- dmabuf->ready = 0;
- dmabuf->swptr = dmabuf->hwptr = 0;
- dmabuf->count = dmabuf->total_bytes = 0;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- synchronize_irq(card->irq);
- dmabuf->ready = 0;
- dmabuf->swptr = dmabuf->hwptr = 0;
- dmabuf->count = dmabuf->total_bytes = 0;
- }
- break;
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- ret = drain_dac(state, file->f_flags & O_NONBLOCK);
- break;
-
- case SNDCTL_DSP_SPEED: /* set smaple rate */
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if (val >= 0) {
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- dmabuf->ready = 0;
- spin_lock_irqsave(&state->card->lock, flags);
- trident_set_dac_rate(state, val);
- spin_unlock_irqrestore(&state->card->lock, flags);
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- dmabuf->ready = 0;
- spin_lock_irqsave(&state->card->lock, flags);
- trident_set_adc_rate(state, val);
- spin_unlock_irqrestore(&state->card->lock, flags);
- }
- }
- ret = put_user(dmabuf->rate, p);
- break;
-
- case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if ((ret = lock_set_fmt(state)) < 0)
- return ret;
-
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- dmabuf->ready = 0;
- if (val)
- dmabuf->fmt |= TRIDENT_FMT_STEREO;
- else
- dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- dmabuf->ready = 0;
- if (val)
- dmabuf->fmt |= TRIDENT_FMT_STEREO;
- else
- dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
- }
- unlock_set_fmt(state);
- break;
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE) {
- if ((val = prog_dmabuf_playback(state)))
- ret = val;
- else
- ret = put_user(dmabuf->fragsize, p);
- break;
- }
- if (file->f_mode & FMODE_READ) {
- if ((val = prog_dmabuf_record(state)))
- ret = val;
- else
- ret = put_user(dmabuf->fragsize, p);
- break;
- }
- /* neither READ nor WRITE? is this even possible? */
- ret = -EINVAL;
- break;
-
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */
- ret = put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
- AFMT_U8, p);
- break;
-
- case SNDCTL_DSP_SETFMT: /* Select sample format */
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if ((ret = lock_set_fmt(state)) < 0)
- return ret;
-
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- dmabuf->ready = 0;
- if (val == AFMT_S16_LE)
- dmabuf->fmt |= TRIDENT_FMT_16BIT;
- else
- dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- dmabuf->ready = 0;
- if (val == AFMT_S16_LE)
- dmabuf->fmt |= TRIDENT_FMT_16BIT;
- else
- dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
- }
- }
- unlock_set_fmt(state);
- ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE :
- AFMT_U8, p);
- break;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if (val != 0) {
- if ((ret = lock_set_fmt(state)) < 0)
- return ret;
-
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- dmabuf->ready = 0;
-
- //prevent from memory leak
- if ((state->chans_num > 2) && (state->chans_num != val)) {
- ali_free_other_states_resources(state);
- state->chans_num = 1;
- }
-
- if (val >= 2) {
-
- dmabuf->fmt |= TRIDENT_FMT_STEREO;
- if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) {
- if (card->rec_channel_use_count > 0) {
- printk(KERN_ERR "trident: Record is "
- "working on the card!\n");
- ret = -EBUSY;
- unlock_set_fmt(state);
- break;
- }
-
- ret = ali_setup_multi_channels(state->card, 6);
- if (ret < 0) {
- unlock_set_fmt(state);
- break;
- }
- mutex_lock(&state->card->open_mutex);
- ret = ali_allocate_other_states_resources(state, 6);
- if (ret < 0) {
- mutex_unlock(&state->card->open_mutex);
- unlock_set_fmt(state);
- break;
- }
- state->card->multi_channel_use_count++;
- mutex_unlock(&state->card->open_mutex);
- } else
- val = 2; /*yield to 2-channels */
- } else
- dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
- state->chans_num = val;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- dmabuf->ready = 0;
- if (val >= 2) {
- if (!((file->f_mode & FMODE_WRITE) &&
- (val == 6)))
- val = 2;
- dmabuf->fmt |= TRIDENT_FMT_STEREO;
- } else
- dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
- state->chans_num = val;
- }
- unlock_set_fmt(state);
- }
- ret = put_user(val, p);
- break;
-
- case SNDCTL_DSP_POST:
- /* Cause the working fragment to be output */
- break;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if (dmabuf->subdivision) {
- ret = -EINVAL;
- break;
- }
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if (val != 1 && val != 2 && val != 4) {
- ret = -EINVAL;
- break;
- }
- dmabuf->subdivision = val;
- break;
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
-
- dmabuf->ossfragshift = val & 0xffff;
- dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
- if (dmabuf->ossfragshift < 4)
- dmabuf->ossfragshift = 4;
- if (dmabuf->ossfragshift > 15)
- dmabuf->ossfragshift = 15;
- if (dmabuf->ossmaxfrags < 4)
- dmabuf->ossmaxfrags = 4;
-
- break;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE)) {
- ret = -EINVAL;
- break;
- }
- if (!dmabuf->ready && (val = prog_dmabuf_playback(state)) != 0) {
- ret = val;
- break;
- }
- spin_lock_irqsave(&state->card->lock, flags);
- trident_update_ptr(state);
- abinfo.fragsize = dmabuf->fragsize;
- abinfo.bytes = dmabuf->dmasize - dmabuf->count;
- abinfo.fragstotal = dmabuf->numfrag;
- abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
- spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user(argp, &abinfo, sizeof (abinfo)) ?
- -EFAULT : 0;
- break;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ)) {
- ret = -EINVAL;
- break;
- }
- if (!dmabuf->ready && (val = prog_dmabuf_record(state)) != 0) {
- ret = val;
- break;
- }
- spin_lock_irqsave(&state->card->lock, flags);
- trident_update_ptr(state);
- abinfo.fragsize = dmabuf->fragsize;
- abinfo.bytes = dmabuf->count;
- abinfo.fragstotal = dmabuf->numfrag;
- abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
- spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user(argp, &abinfo, sizeof (abinfo)) ?
- -EFAULT : 0;
- break;
-
- case SNDCTL_DSP_NONBLOCK:
- file->f_flags |= O_NONBLOCK;
- break;
-
- case SNDCTL_DSP_GETCAPS:
- ret = put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
- DSP_CAP_MMAP | DSP_CAP_BIND, p);
- break;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- if ((file->f_mode & FMODE_READ) && dmabuf->enable)
- val |= PCM_ENABLE_INPUT;
- if ((file->f_mode & FMODE_WRITE) && dmabuf->enable)
- val |= PCM_ENABLE_OUTPUT;
- ret = put_user(val, p);
- break;
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- if (!dmabuf->ready &&
- (ret = prog_dmabuf_record(state)))
- break;
- start_adc(state);
- } else
- stop_adc(state);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- if (!dmabuf->ready &&
- (ret = prog_dmabuf_playback(state)))
- break;
- start_dac(state);
- } else
- stop_dac(state);
- }
- break;
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ)) {
- ret = -EINVAL;
- break;
- }
- if (!dmabuf->ready && (val = prog_dmabuf_record(state))
- != 0) {
- ret = val;
- break;
- }
- spin_lock_irqsave(&state->card->lock, flags);
- trident_update_ptr(state);
- cinfo.bytes = dmabuf->total_bytes;
- cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
- cinfo.ptr = dmabuf->hwptr;
- if (dmabuf->mapped)
- dmabuf->count &= dmabuf->fragsize - 1;
- spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user(argp, &cinfo, sizeof (cinfo)) ?
- -EFAULT : 0;
- break;
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_WRITE)) {
- ret = -EINVAL;
- break;
- }
- if (!dmabuf->ready && (val = prog_dmabuf_playback(state))
- != 0) {
- ret = val;
- break;
- }
-
- spin_lock_irqsave(&state->card->lock, flags);
- trident_update_ptr(state);
- cinfo.bytes = dmabuf->total_bytes;
- cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
- cinfo.ptr = dmabuf->hwptr;
- if (dmabuf->mapped)
- dmabuf->count &= dmabuf->fragsize - 1;
- spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user(argp, &cinfo, sizeof (cinfo)) ?
- -EFAULT : 0;
- break;
-
- case SNDCTL_DSP_SETDUPLEX:
- ret = -EINVAL;
- break;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE)) {
- ret = -EINVAL;
- break;
- }
- if (!dmabuf->ready && (val = prog_dmabuf_playback(state)) != 0) {
- ret = val;
- break;
- }
- spin_lock_irqsave(&state->card->lock, flags);
- trident_update_ptr(state);
- val = dmabuf->count;
- spin_unlock_irqrestore(&state->card->lock, flags);
- ret = put_user(val, p);
- break;
-
- case SOUND_PCM_READ_RATE:
- ret = put_user(dmabuf->rate, p);
- break;
-
- case SOUND_PCM_READ_CHANNELS:
- ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- p);
- break;
-
- case SOUND_PCM_READ_BITS:
- ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE :
- AFMT_U8, p);
- break;
-
- case SNDCTL_DSP_GETCHANNELMASK:
- ret = put_user(DSP_BIND_FRONT | DSP_BIND_SURR |
- DSP_BIND_CENTER_LFE, p);
- break;
-
- case SNDCTL_DSP_BIND_CHANNEL:
- if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
- ret = -EINVAL;
- break;
- }
-
- if (get_user(val, p)) {
- ret = -EFAULT;
- break;
- }
- if (val == DSP_BIND_QUERY) {
- val = dmabuf->channel->attribute | 0x3c00;
- val = attr2mask[val >> 8];
- } else {
- dmabuf->ready = 0;
- if (file->f_mode & FMODE_READ)
- dmabuf->channel->attribute = (CHANNEL_REC |
- SRC_ENABLE);
- if (file->f_mode & FMODE_WRITE)
- dmabuf->channel->attribute = (CHANNEL_SPC_PB |
- SRC_ENABLE);
- dmabuf->channel->attribute |= mask2attr[ffs(val)];
- }
- ret = put_user(val, p);
- break;
-
- case SNDCTL_DSP_MAPINBUF:
- case SNDCTL_DSP_MAPOUTBUF:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_WRITE_FILTER:
- case SOUND_PCM_READ_FILTER:
- default:
- ret = -EINVAL;
- break;
-
- }
- return ret;
-}
-
-static int
-trident_open(struct inode *inode, struct file *file)
-{
- int i = 0;
- int minor = iminor(inode);
- struct trident_card *card = devs;
- struct trident_state *state = NULL;
- struct dmabuf *dmabuf = NULL;
- unsigned long flags;
-
- /* Added by Matt Wu 01-05-2001 */
- /* TODO: there's some redundacy here wrt the check below */
- /* for multi_use_count > 0. Should we return -EBUSY or find */
- /* a different card? for now, don't break current behaviour */
- /* -- mulix */
- if (file->f_mode & FMODE_READ) {
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- if (card->multi_channel_use_count > 0)
- return -EBUSY;
- }
- }
-
- /* find an available virtual channel (instance of /dev/dsp) */
- while (card != NULL) {
- mutex_lock(&card->open_mutex);
- if (file->f_mode & FMODE_READ) {
- /* Skip opens on cards that are in 6 channel mode */
- if (card->multi_channel_use_count > 0) {
- mutex_unlock(&card->open_mutex);
- card = card->next;
- continue;
- }
- }
- for (i = 0; i < NR_HW_CH; i++) {
- if (card->states[i] == NULL) {
- state = card->states[i] = kzalloc(sizeof(*state), GFP_KERNEL);
- if (state == NULL) {
- mutex_unlock(&card->open_mutex);
- return -ENOMEM;
- }
- mutex_init(&state->sem);
- dmabuf = &state->dmabuf;
- goto found_virt;
- }
- }
- mutex_unlock(&card->open_mutex);
- card = card->next;
- }
- /* no more virtual channel avaiable */
- if (!state) {
- return -ENODEV;
- }
- found_virt:
- /* found a free virtual channel, allocate hardware channels */
- if (file->f_mode & FMODE_READ)
- dmabuf->channel = card->alloc_rec_pcm_channel(card);
- else
- dmabuf->channel = card->alloc_pcm_channel(card);
-
- if (dmabuf->channel == NULL) {
- kfree(card->states[i]);
- card->states[i] = NULL;
- return -ENODEV;
- }
-
- /* initialize the virtual channel */
- state->virt = i;
- state->card = card;
- state->magic = TRIDENT_STATE_MAGIC;
- init_waitqueue_head(&dmabuf->wait);
- file->private_data = state;
-
- /* set default sample format. According to OSS Programmer's */
- /* Guide /dev/dsp should be default to unsigned 8-bits, mono, */
- /* with sample rate 8kHz and /dev/dspW will accept 16-bits sample */
- if (file->f_mode & FMODE_WRITE) {
- dmabuf->fmt &= ~TRIDENT_FMT_MASK;
- if ((minor & 0x0f) == SND_DEV_DSP16)
- dmabuf->fmt |= TRIDENT_FMT_16BIT;
- dmabuf->ossfragshift = 0;
- dmabuf->ossmaxfrags = 0;
- dmabuf->subdivision = 0;
- if (card->pci_id == PCI_DEVICE_ID_SI_7018) {
- /* set default channel attribute to normal playback */
- dmabuf->channel->attribute = CHANNEL_PB;
- }
- spin_lock_irqsave(&card->lock, flags);
- trident_set_dac_rate(state, 8000);
- spin_unlock_irqrestore(&card->lock, flags);
- }
-
- if (file->f_mode & FMODE_READ) {
- /* FIXME: Trident 4d can only record in signed 16-bits stereo, */
- /* 48kHz sample, to be dealed with in trident_set_adc_rate() ?? */
- dmabuf->fmt &= ~TRIDENT_FMT_MASK;
- if ((minor & 0x0f) == SND_DEV_DSP16)
- dmabuf->fmt |= TRIDENT_FMT_16BIT;
- dmabuf->ossfragshift = 0;
- dmabuf->ossmaxfrags = 0;
- dmabuf->subdivision = 0;
- if (card->pci_id == PCI_DEVICE_ID_SI_7018) {
- /* set default channel attribute to 0x8a80, record from
- PCM L/R FIFO and mono = (left + right + 1)/2 */
- dmabuf->channel->attribute = (CHANNEL_REC | PCM_LR |
- MONO_MIX);
- }
- spin_lock_irqsave(&card->lock, flags);
- trident_set_adc_rate(state, 8000);
- spin_unlock_irqrestore(&card->lock, flags);
-
- /* Added by Matt Wu 01-05-2001 */
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451)
- card->rec_channel_use_count++;
- }
-
- state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_unlock(&card->open_mutex);
-
- pr_debug("trident: open virtual channel %d, hard channel %d\n",
- state->virt, dmabuf->channel->num);
-
- return nonseekable_open(inode, file);
-}
-
-static int
-trident_release(struct inode *inode, struct file *file)
-{
- struct trident_state *state = (struct trident_state *)file->private_data;
- struct trident_card *card;
- struct dmabuf *dmabuf;
-
- VALIDATE_STATE(state);
-
- card = state->card;
- dmabuf = &state->dmabuf;
-
- if (file->f_mode & FMODE_WRITE) {
- trident_clear_tail(state);
- drain_dac(state, file->f_flags & O_NONBLOCK);
- }
-
- pr_debug("trident: closing virtual channel %d, hard channel %d\n",
- state->virt, dmabuf->channel->num);
-
- /* stop DMA state machine and free DMA buffers/channels */
- mutex_lock(&card->open_mutex);
-
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- dealloc_dmabuf(&state->dmabuf, state->card->pci_dev);
- state->card->free_pcm_channel(state->card, dmabuf->channel->num);
-
- /* Added by Matt Wu */
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- if (state->chans_num > 2) {
- if (card->multi_channel_use_count-- < 0)
- card->multi_channel_use_count = 0;
- if (card->multi_channel_use_count == 0)
- ali_close_multi_channels();
- ali_free_other_states_resources(state);
- }
- }
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(state);
- dealloc_dmabuf(&state->dmabuf, state->card->pci_dev);
- state->card->free_pcm_channel(state->card, dmabuf->channel->num);
-
- /* Added by Matt Wu */
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- if (card->rec_channel_use_count-- < 0)
- card->rec_channel_use_count = 0;
- }
- }
-
- card->states[state->virt] = NULL;
- kfree(state);
-
- /* we're covered by the open_mutex */
- mutex_unlock(&card->open_mutex);
-
- return 0;
-}
-
-static const struct file_operations trident_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = trident_read,
- .write = trident_write,
- .poll = trident_poll,
- .ioctl = trident_ioctl,
- .mmap = trident_mmap,
- .open = trident_open,
- .release = trident_release,
-};
-
-/* trident specific AC97 functions */
-/* Write AC97 codec registers */
-static void
-trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
-{
- struct trident_card *card = (struct trident_card *)codec->private_data;
- unsigned int address, mask, busy;
- unsigned short count = 0xffff;
- unsigned long flags;
- u32 data;
-
- data = ((u32) val) << 16;
-
- switch (card->pci_id) {
- default:
- case PCI_DEVICE_ID_SI_7018:
- address = SI_AC97_WRITE;
- mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY;
- if (codec->id)
- mask |= SI_AC97_SECONDARY;
- busy = SI_AC97_BUSY_WRITE;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- address = DX_ACR0_AC97_W;
- mask = busy = DX_AC97_BUSY_WRITE;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- address = NX_ACR1_AC97_W;
- mask = NX_AC97_BUSY_WRITE;
- if (codec->id)
- mask |= NX_AC97_WRITE_SECONDARY;
- busy = NX_AC97_BUSY_WRITE;
- break;
- case PCI_DEVICE_ID_INTERG_5050:
- address = SI_AC97_WRITE;
- mask = busy = SI_AC97_BUSY_WRITE;
- if (codec->id)
- mask |= SI_AC97_SECONDARY;
- break;
- }
-
- spin_lock_irqsave(&card->lock, flags);
- do {
- if ((inw(TRID_REG(card, address)) & busy) == 0)
- break;
- } while (--count);
-
- data |= (mask | (reg & AC97_REG_ADDR));
-
- if (count == 0) {
- printk(KERN_ERR "trident: AC97 CODEC write timed out.\n");
- spin_unlock_irqrestore(&card->lock, flags);
- return;
- }
-
- outl(data, TRID_REG(card, address));
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-/* Read AC97 codec registers */
-static u16
-trident_ac97_get(struct ac97_codec *codec, u8 reg)
-{
- struct trident_card *card = (struct trident_card *)codec->private_data;
- unsigned int address, mask, busy;
- unsigned short count = 0xffff;
- unsigned long flags;
- u32 data;
-
- switch (card->pci_id) {
- default:
- case PCI_DEVICE_ID_SI_7018:
- address = SI_AC97_READ;
- mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY;
- if (codec->id)
- mask |= SI_AC97_SECONDARY;
- busy = SI_AC97_BUSY_READ;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- address = DX_ACR1_AC97_R;
- mask = busy = DX_AC97_BUSY_READ;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- if (codec->id)
- address = NX_ACR3_AC97_R_SECONDARY;
- else
- address = NX_ACR2_AC97_R_PRIMARY;
- mask = NX_AC97_BUSY_READ;
- busy = NX_AC97_BUSY_READ | NX_AC97_BUSY_DATA;
- break;
- case PCI_DEVICE_ID_INTERG_5050:
- address = SI_AC97_READ;
- mask = busy = SI_AC97_BUSY_READ;
- if (codec->id)
- mask |= SI_AC97_SECONDARY;
- break;
- }
-
- data = (mask | (reg & AC97_REG_ADDR));
-
- spin_lock_irqsave(&card->lock, flags);
- outl(data, TRID_REG(card, address));
- do {
- data = inl(TRID_REG(card, address));
- if ((data & busy) == 0)
- break;
- } while (--count);
- spin_unlock_irqrestore(&card->lock, flags);
-
- if (count == 0) {
- printk(KERN_ERR "trident: AC97 CODEC read timed out.\n");
- data = 0;
- }
- return ((u16) (data >> 16));
-}
-
-/* rewrite ac97 read and write mixer register by hulei for ALI*/
-static int
-acquirecodecaccess(struct trident_card *card)
-{
- u16 wsemamask = 0x6000; /* bit 14..13 */
- u16 wsemabits;
- u16 wcontrol;
- int block = 0;
- int ncount = 25;
- while (1) {
- wcontrol = inw(TRID_REG(card, ALI_AC97_WRITE));
- wsemabits = wcontrol & wsemamask;
-
- if (wsemabits == 0x4000)
- return 1; /* 0x4000 is audio ,then success */
- if (ncount-- < 0)
- break;
- if (wsemabits == 0) {
- unlock:
- outl(((u32) (wcontrol & 0x1eff) | 0x00004000),
- TRID_REG(card, ALI_AC97_WRITE));
- continue;
- }
- udelay(20);
- }
- if (!block) {
- pr_debug("accesscodecsemaphore: try unlock\n");
- block = 1;
- goto unlock;
- }
- return 0;
-}
-
-static void
-releasecodecaccess(struct trident_card *card)
-{
- unsigned long wcontrol;
- wcontrol = inl(TRID_REG(card, ALI_AC97_WRITE));
- outl((wcontrol & 0xffff1eff), TRID_REG(card, ALI_AC97_WRITE));
-}
-
-static int
-waitforstimertick(struct trident_card *card)
-{
- unsigned long chk1, chk2;
- unsigned int wcount = 0xffff;
- chk1 = inl(TRID_REG(card, ALI_STIMER));
-
- while (1) {
- chk2 = inl(TRID_REG(card, ALI_STIMER));
- if ((wcount > 0) && chk1 != chk2)
- return 1;
- if (wcount <= 0)
- break;
- udelay(50);
- }
- return 0;
-}
-
-/* Read AC97 codec registers for ALi*/
-static u16
-ali_ac97_get(struct trident_card *card, int secondary, u8 reg)
-{
- unsigned int address, mask;
- unsigned int ncount;
- unsigned long aud_reg;
- u32 data;
- u16 wcontrol;
- unsigned long flags;
-
- BUG_ON(!card);
-
- address = ALI_AC97_READ;
- if (card->revision == ALI_5451_V02) {
- address = ALI_AC97_WRITE;
- }
- mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY;
- if (secondary)
- mask |= ALI_AC97_SECONDARY;
-
- spin_lock_irqsave(&card->lock, flags);
-
- if (!acquirecodecaccess(card))
- printk(KERN_ERR "access codec fail\n");
-
- wcontrol = inw(TRID_REG(card, ALI_AC97_WRITE));
- wcontrol &= 0xfe00;
- wcontrol |= (0x8000 | reg);
- outw(wcontrol, TRID_REG(card, ALI_AC97_WRITE));
-
- data = (mask | (reg & AC97_REG_ADDR));
-
- if (!waitforstimertick(card)) {
- printk(KERN_ERR "ali_ac97_read: BIT_CLOCK is dead\n");
- goto releasecodec;
- }
-
- udelay(20);
-
- ncount = 10;
-
- while (1) {
- if ((inw(TRID_REG(card, ALI_AC97_WRITE)) & ALI_AC97_BUSY_READ)
- != 0)
- break;
- if (ncount <= 0)
- break;
- if (ncount-- == 1) {
- pr_debug("ali_ac97_read :try clear busy flag\n");
- aud_reg = inl(TRID_REG(card, ALI_AC97_WRITE));
- outl((aud_reg & 0xffff7fff),
- TRID_REG(card, ALI_AC97_WRITE));
- }
- udelay(10);
- }
-
- data = inl(TRID_REG(card, address));
-
- spin_unlock_irqrestore(&card->lock, flags);
-
- return ((u16) (data >> 16));
-
- releasecodec:
- releasecodecaccess(card);
- spin_unlock_irqrestore(&card->lock, flags);
- printk(KERN_ERR "ali_ac97_read: AC97 CODEC read timed out.\n");
- return 0;
-}
-
-/* Write AC97 codec registers for hulei*/
-static void
-ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val)
-{
- unsigned int address, mask;
- unsigned int ncount;
- u32 data;
- u16 wcontrol;
- unsigned long flags;
-
- data = ((u32) val) << 16;
-
- BUG_ON(!card);
-
- address = ALI_AC97_WRITE;
- mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
- if (secondary)
- mask |= ALI_AC97_SECONDARY;
- if (card->revision == ALI_5451_V02)
- mask |= ALI_AC97_WRITE_MIXER_REGISTER;
-
- spin_lock_irqsave(&card->lock, flags);
- if (!acquirecodecaccess(card))
- printk(KERN_ERR "ali_ac97_write: access codec fail\n");
-
- wcontrol = inw(TRID_REG(card, ALI_AC97_WRITE));
- wcontrol &= 0xff00;
- wcontrol |= (0x8100 | reg); /* bit 8=1: (ali1535 )reserved/ */
- /* ali1535+ write */
- outl((data | wcontrol), TRID_REG(card, ALI_AC97_WRITE));
-
- if (!waitforstimertick(card)) {
- printk(KERN_ERR "BIT_CLOCK is dead\n");
- goto releasecodec;
- }
-
- ncount = 10;
- while (1) {
- wcontrol = inw(TRID_REG(card, ALI_AC97_WRITE));
- if (!(wcontrol & 0x8000))
- break;
- if (ncount <= 0)
- break;
- if (ncount-- == 1) {
- pr_debug("ali_ac97_set :try clear busy flag!!\n");
- outw(wcontrol & 0x7fff,
- TRID_REG(card, ALI_AC97_WRITE));
- }
- udelay(10);
- }
-
- releasecodec:
- releasecodecaccess(card);
- spin_unlock_irqrestore(&card->lock, flags);
- return;
-}
-
-static void
-ali_enable_special_channel(struct trident_state *stat)
-{
- struct trident_card *card = stat->card;
- unsigned long s_channels;
-
- s_channels = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
- s_channels |= (1 << stat->dmabuf.channel->num);
- outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL));
-}
-
-static u16
-ali_ac97_read(struct ac97_codec *codec, u8 reg)
-{
- int id;
- u16 data;
- struct trident_card *card = NULL;
-
- /* Added by Matt Wu */
- BUG_ON(!codec);
-
- card = (struct trident_card *) codec->private_data;
-
- if (!card->mixer_regs_ready)
- return ali_ac97_get(card, codec->id, reg);
-
- /*
- * FIXME: need to stop this caching some registers
- */
- if (codec->id)
- id = 1;
- else
- id = 0;
-
- data = card->mixer_regs[reg / 2][id];
- return data;
-}
-
-static void
-ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
-{
- int id;
- struct trident_card *card;
-
- /* Added by Matt Wu */
- BUG_ON(!codec);
-
- card = (struct trident_card *) codec->private_data;
-
- if (!card->mixer_regs_ready) {
- ali_ac97_set(card, codec->id, reg, val);
- return;
- }
-
- if (codec->id)
- id = 1;
- else
- id = 0;
-
- card->mixer_regs[reg / 2][id] = val;
- ali_ac97_set(card, codec->id, reg, val);
-}
-
-/*
-flag: ALI_SPDIF_OUT_TO_SPDIF_OUT
- ALI_PCM_TO_SPDIF_OUT
-*/
-
-static void
-ali_setup_spdif_out(struct trident_card *card, int flag)
-{
- unsigned long spdif;
- unsigned char ch;
-
- char temp;
- struct pci_dev *pci_dev = NULL;
-
- pci_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
- pci_dev);
- if (pci_dev == NULL)
- return;
- pci_read_config_byte(pci_dev, 0x61, &temp);
- temp |= 0x40;
- pci_write_config_byte(pci_dev, 0x61, temp);
- pci_read_config_byte(pci_dev, 0x7d, &temp);
- temp |= 0x01;
- pci_write_config_byte(pci_dev, 0x7d, temp);
- pci_read_config_byte(pci_dev, 0x7e, &temp);
- temp &= (~0x20);
- temp |= 0x10;
- pci_write_config_byte(pci_dev, 0x7e, temp);
-
- pci_dev_put(pci_dev);
-
- ch = inb(TRID_REG(card, ALI_SCTRL));
- outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL));
- ch = inb(TRID_REG(card, ALI_SPDIF_CTRL));
- outb(ch & ALI_SPDIF_OUT_CH_STATUS, TRID_REG(card, ALI_SPDIF_CTRL));
-
- if (flag & ALI_SPDIF_OUT_TO_SPDIF_OUT) {
- spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
- spdif |= ALI_SPDIF_OUT_CH_ENABLE;
- spdif &= ALI_SPDIF_OUT_SEL_SPDIF;
- outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
- spdif = inw(TRID_REG(card, ALI_SPDIF_CS));
- if (flag & ALI_SPDIF_OUT_NON_PCM)
- spdif |= 0x0002;
- else
- spdif &= (~0x0002);
- outw(spdif, TRID_REG(card, ALI_SPDIF_CS));
- } else {
- spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
- spdif |= ALI_SPDIF_OUT_SEL_PCM;
- outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
- }
-}
-
-static void
-ali_disable_special_channel(struct trident_card *card, int ch)
-{
- unsigned long sc;
-
- sc = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
- sc &= ~(1 << ch);
- outl(sc, TRID_REG(card, ALI_GLOBAL_CONTROL));
-}
-
-static void
-ali_disable_spdif_in(struct trident_card *card)
-{
- unsigned long spdif;
-
- spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
- spdif &= (~ALI_SPDIF_IN_SUPPORT);
- outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
-
- ali_disable_special_channel(card, ALI_SPDIF_IN_CHANNEL);
-}
-
-static void
-ali_setup_spdif_in(struct trident_card *card)
-{
- unsigned long spdif;
-
- //Set SPDIF IN Supported
- spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
- spdif |= ALI_SPDIF_IN_SUPPORT;
- outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
-
- //Set SPDIF IN Rec
- spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
- spdif |= ALI_SPDIF_IN_CH_ENABLE;
- outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
-
- spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
- spdif |= ALI_SPDIF_IN_CH_STATUS;
- outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
-/*
- spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
- spdif |= ALI_SPDIF_IN_FUNC_ENABLE;
- outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
-*/
-}
-
-static void
-ali_delay(struct trident_card *card, int interval)
-{
- unsigned long begintimer, currenttimer;
-
- begintimer = inl(TRID_REG(card, ALI_STIMER));
- currenttimer = inl(TRID_REG(card, ALI_STIMER));
-
- while (currenttimer < begintimer + interval)
- currenttimer = inl(TRID_REG(card, ALI_STIMER));
-}
-
-static void
-ali_detect_spdif_rate(struct trident_card *card)
-{
- u16 wval = 0;
- u16 count = 0;
- u8 bval = 0, R1 = 0, R2 = 0;
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CTRL));
- bval |= 0x02;
- outb(bval, TRID_REG(card, ALI_SPDIF_CTRL));
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
- bval |= 0x1F;
- outb(bval, TRID_REG(card, ALI_SPDIF_CTRL + 1));
-
- while (((R1 < 0x0B) || (R1 > 0x0E)) && (R1 != 0x12) &&
- count <= 50000) {
- count++;
-
- ali_delay(card, 6);
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
- R1 = bval & 0x1F;
- }
-
- if (count > 50000) {
- printk(KERN_WARNING "trident: Error in "
- "ali_detect_spdif_rate!\n");
- return;
- }
-
- count = 0;
-
- while (count <= 50000) {
- count++;
-
- ali_delay(card, 6);
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
- R2 = bval & 0x1F;
-
- if (R2 != R1)
- R1 = R2;
- else
- break;
- }
-
- if (count > 50000) {
- printk(KERN_WARNING "trident: Error in "
- "ali_detect_spdif_rate!\n");
- return;
- }
-
- switch (R2) {
- case 0x0b:
- case 0x0c:
- case 0x0d:
- case 0x0e:
- wval = inw(TRID_REG(card, ALI_SPDIF_CTRL + 2));
- wval &= 0xE0F0;
- wval |= (u16) 0x09 << 8 | (u16) 0x05;
- outw(wval, TRID_REG(card, ALI_SPDIF_CTRL + 2));
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CS + 3)) & 0xF0;
- outb(bval | 0x02, TRID_REG(card, ALI_SPDIF_CS + 3));
- break;
-
- case 0x12:
- wval = inw(TRID_REG(card, ALI_SPDIF_CTRL + 2));
- wval &= 0xE0F0;
- wval |= (u16) 0x0E << 8 | (u16) 0x08;
- outw(wval, TRID_REG(card, ALI_SPDIF_CTRL + 2));
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CS + 3)) & 0xF0;
- outb(bval | 0x03, TRID_REG(card, ALI_SPDIF_CS + 3));
- break;
-
- default:
- break;
- }
-
-}
-
-static unsigned int
-ali_get_spdif_in_rate(struct trident_card *card)
-{
- u32 dwRate = 0;
- u8 bval = 0;
-
- ali_detect_spdif_rate(card);
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CTRL));
- bval &= 0x7F;
- bval |= 0x40;
- outb(bval, TRID_REG(card, ALI_SPDIF_CTRL));
-
- bval = inb(TRID_REG(card, ALI_SPDIF_CS + 3));
- bval &= 0x0F;
-
- switch (bval) {
- case 0:
- dwRate = 44100;
- break;
- case 1:
- dwRate = 48000;
- break;
- case 2:
- dwRate = 32000;
- break;
- default:
- // Error occurs
- break;
- }
-
- return dwRate;
-
-}
-
-static int
-ali_close_multi_channels(void)
-{
- char temp = 0;
- struct pci_dev *pci_dev = NULL;
-
- pci_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
- pci_dev);
- if (pci_dev == NULL)
- return -1;
-
- pci_read_config_byte(pci_dev, 0x59, &temp);
- temp &= ~0x80;
- pci_write_config_byte(pci_dev, 0x59, temp);
-
- pci_dev_put(pci_dev);
-
- pci_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,
- NULL);
- if (pci_dev == NULL)
- return -1;
-
- pci_read_config_byte(pci_dev, 0xB8, &temp);
- temp &= ~0x20;
- pci_write_config_byte(pci_dev, 0xB8, temp);
-
- pci_dev_put(pci_dev);
-
- return 0;
-}
-
-static int
-ali_setup_multi_channels(struct trident_card *card, int chan_nums)
-{
- unsigned long dwValue;
- char temp = 0;
- struct pci_dev *pci_dev = NULL;
-
- pci_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
- pci_dev);
- if (pci_dev == NULL)
- return -1;
- pci_read_config_byte(pci_dev, 0x59, &temp);
- temp |= 0x80;
- pci_write_config_byte(pci_dev, 0x59, temp);
-
- pci_dev_put(pci_dev);
-
- pci_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,
- NULL);
- if (pci_dev == NULL)
- return -1;
- pci_read_config_byte(pci_dev, (int) 0xB8, &temp);
- temp |= 0x20;
- pci_write_config_byte(pci_dev, (int) 0xB8, (u8) temp);
-
- pci_dev_put(pci_dev);
-
- if (chan_nums == 6) {
- dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000;
- outl(dwValue, TRID_REG(card, ALI_SCTRL));
- mdelay(4);
- dwValue = inl(TRID_REG(card, ALI_SCTRL));
- if (dwValue & 0x2000000) {
- ali_ac97_write(card->ac97_codec[0], 0x02, 8080);
- ali_ac97_write(card->ac97_codec[0], 0x36, 0);
- ali_ac97_write(card->ac97_codec[0], 0x38, 0);
- /*
- * On a board with a single codec you won't get the
- * surround. On other boards configure it.
- */
- if (card->ac97_codec[1] != NULL) {
- ali_ac97_write(card->ac97_codec[1], 0x36, 0);
- ali_ac97_write(card->ac97_codec[1], 0x38, 0);
- ali_ac97_write(card->ac97_codec[1], 0x02, 0x0606);
- ali_ac97_write(card->ac97_codec[1], 0x18, 0x0303);
- ali_ac97_write(card->ac97_codec[1], 0x74, 0x3);
- }
- return 1;
- }
- }
- return -EINVAL;
-}
-
-static void
-ali_free_pcm_channel(struct trident_card *card, unsigned int channel)
-{
- int bank;
-
- if (channel > 31)
- return;
-
- bank = channel >> 5;
- channel = channel & 0x1f;
-
- card->banks[bank].bitmap &= ~(1 << (channel));
-}
-
-static int
-ali_allocate_other_states_resources(struct trident_state *state, int chan_nums)
-{
- struct trident_card *card = state->card;
- struct trident_state *s;
- int i, state_count = 0;
- struct trident_pcm_bank *bank;
- struct trident_channel *channel;
- unsigned long num;
-
- bank = &card->banks[BANK_A];
-
- if (chan_nums != 6)
- return 0;
-
- for (i = 0; (i < ALI_CHANNELS) && (state_count != 4); i++) {
- if (card->states[i])
- continue;
-
- num = ali_multi_channels_5_1[state_count];
- if (!(bank->bitmap & (1 << num))) {
- bank->bitmap |= 1 << num;
- channel = &bank->channels[num];
- channel->num = num;
- } else {
- state_count--;
- for (; state_count >= 0; state_count--) {
- kfree(state->other_states[state_count]);
- num = ali_multi_channels_5_1[state_count];
- ali_free_pcm_channel(card, num);
- }
- return -EBUSY;
- }
- s = card->states[i] = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!s) {
- num = ali_multi_channels_5_1[state_count];
- ali_free_pcm_channel(card, num);
- state_count--;
- for (; state_count >= 0; state_count--) {
- num = ali_multi_channels_5_1[state_count];
- ali_free_pcm_channel(card, num);
- kfree(state->other_states[state_count]);
- }
- return -ENOMEM;
- }
-
- s->dmabuf.channel = channel;
- s->dmabuf.ossfragshift = s->dmabuf.ossmaxfrags =
- s->dmabuf.subdivision = 0;
- init_waitqueue_head(&s->dmabuf.wait);
- s->magic = card->magic;
- s->card = card;
- s->virt = i;
- ali_enable_special_channel(s);
- state->other_states[state_count++] = s;
- }
-
- if (state_count != 4) {
- state_count--;
- for (; state_count >= 0; state_count--) {
- kfree(state->other_states[state_count]);
- num = ali_multi_channels_5_1[state_count];
- ali_free_pcm_channel(card, num);
- }
- return -EBUSY;
- }
- return 0;
-}
-
-#ifdef CONFIG_PM
-/* save registers for ALi Power Management */
-static struct ali_saved_registers {
- unsigned long global_regs[ALI_GLOBAL_REGS];
- unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
- unsigned mixer_regs[ALI_MIXER_REGS];
-} ali_registers;
-
-static void
-ali_save_regs(struct trident_card *card)
-{
- unsigned long flags;
- int i, j;
-
- spin_lock_irqsave(&card->lock, flags);
-
- ali_registers.global_regs[0x2c] = inl(TRID_REG(card, T4D_MISCINT));
- //ali_registers.global_regs[0x20] = inl(TRID_REG(card,T4D_START_A));
- ali_registers.global_regs[0x21] = inl(TRID_REG(card, T4D_STOP_A));
-
- //disable all IRQ bits
- outl(ALI_DISABLE_ALL_IRQ, TRID_REG(card, T4D_MISCINT));
-
- for (i = 1; i < ALI_MIXER_REGS; i++)
- ali_registers.mixer_regs[i] = ali_ac97_read(card->ac97_codec[0],
- i * 2);
-
- for (i = 0; i < ALI_GLOBAL_REGS; i++) {
- if ((i * 4 == T4D_MISCINT) || (i * 4 == T4D_STOP_A))
- continue;
- ali_registers.global_regs[i] = inl(TRID_REG(card, i * 4));
- }
-
- for (i = 0; i < ALI_CHANNELS; i++) {
- outb(i, TRID_REG(card, T4D_LFO_GC_CIR));
- for (j = 0; j < ALI_CHANNEL_REGS; j++)
- ali_registers.channel_regs[i][j] = inl(TRID_REG(card,
- j * 4 + 0xe0));
- }
-
- //Stop all HW channel
- outl(ALI_STOP_ALL_CHANNELS, TRID_REG(card, T4D_STOP_A));
-
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-static void
-ali_restore_regs(struct trident_card *card)
-{
- unsigned long flags;
- int i, j;
-
- spin_lock_irqsave(&card->lock, flags);
-
- for (i = 1; i < ALI_MIXER_REGS; i++)
- ali_ac97_write(card->ac97_codec[0], i * 2,
- ali_registers.mixer_regs[i]);
-
- for (i = 0; i < ALI_CHANNELS; i++) {
- outb(i, TRID_REG(card, T4D_LFO_GC_CIR));
- for (j = 0; j < ALI_CHANNEL_REGS; j++)
- outl(ali_registers.channel_regs[i][j],
- TRID_REG(card, j * 4 + 0xe0));
- }
-
- for (i = 0; i < ALI_GLOBAL_REGS; i++) {
- if ((i * 4 == T4D_MISCINT) || (i * 4 == T4D_STOP_A) ||
- (i * 4 == T4D_START_A))
- continue;
- outl(ali_registers.global_regs[i], TRID_REG(card, i * 4));
- }
-
- //start HW channel
- outl(ali_registers.global_regs[0x20], TRID_REG(card, T4D_START_A));
- //restore IRQ enable bits
- outl(ali_registers.global_regs[0x2c], TRID_REG(card, T4D_MISCINT));
-
- spin_unlock_irqrestore(&card->lock, flags);
-}
-
-static int
-trident_suspend(struct pci_dev *dev, pm_message_t unused)
-{
- struct trident_card *card = pci_get_drvdata(dev);
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- ali_save_regs(card);
- }
- return 0;
-}
-
-static int
-trident_resume(struct pci_dev *dev)
-{
- struct trident_card *card = pci_get_drvdata(dev);
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- ali_restore_regs(card);
- }
- return 0;
-}
-#endif
-
-static struct trident_channel *
-ali_alloc_pcm_channel(struct trident_card *card)
-{
- struct trident_pcm_bank *bank;
- int idx;
-
- bank = &card->banks[BANK_A];
-
- if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) &
- (ALI_SPDIF_OUT_CH_ENABLE)) {
- idx = ALI_SPDIF_OUT_CHANNEL;
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx;
- return channel;
- }
- }
-
- for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST;
- idx++) {
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx;
- return channel;
- }
- }
-
- /* no more free channels avaliable */
-#if 0
- printk(KERN_ERR "ali: no more channels available on Bank A.\n");
-#endif /* 0 */
- return NULL;
-}
-
-static struct trident_channel *
-ali_alloc_rec_pcm_channel(struct trident_card *card)
-{
- struct trident_pcm_bank *bank;
- int idx;
-
- if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT)
- idx = ALI_SPDIF_IN_CHANNEL;
- else
- idx = ALI_PCM_IN_CHANNEL;
-
- bank = &card->banks[BANK_A];
-
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- bank->bitmap |= 1 << idx;
- channel->num = idx;
- return channel;
- }
-
- /* no free recordable channels avaliable */
-#if 0
- printk(KERN_ERR "ali: no recordable channels available on Bank A.\n");
-#endif /* 0 */
- return NULL;
-}
-
-static void
-ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate)
-{
- unsigned char ch_st_sel;
- unsigned short status_rate;
-
- switch (rate) {
- case 44100:
- status_rate = 0;
- break;
- case 32000:
- status_rate = 0x300;
- break;
- case 48000:
- default:
- status_rate = 0x200;
- break;
- }
-
- /* select spdif_out */
- ch_st_sel = inb(TRID_REG(card, ALI_SPDIF_CTRL)) & ALI_SPDIF_OUT_CH_STATUS;
-
- ch_st_sel |= 0x80; /* select right */
- outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
- outb(status_rate | 0x20, TRID_REG(card, ALI_SPDIF_CS + 2));
-
- ch_st_sel &= (~0x80); /* select left */
- outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
- outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2));
-}
-
-static void
-ali_address_interrupt(struct trident_card *card)
-{
- int i, channel;
- struct trident_state *state;
- u32 mask, channel_mask;
-
- mask = trident_get_interrupt_mask(card, 0);
- for (i = 0; i < NR_HW_CH; i++) {
- if ((state = card->states[i]) == NULL)
- continue;
- channel = state->dmabuf.channel->num;
- if ((channel_mask = 1 << channel) & mask) {
- mask &= ~channel_mask;
- trident_ack_channel_interrupt(card, channel);
- udelay(100);
- state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE;
- trident_update_ptr(state);
- }
- }
- if (mask) {
- for (i = 0; i < NR_HW_CH; i++) {
- if (mask & (1 << i)) {
- printk("ali: spurious channel irq %d.\n", i);
- trident_ack_channel_interrupt(card, i);
- trident_stop_voice(card, i);
- trident_disable_voice_irq(card, i);
- }
- }
- }
-}
-
-/* Updating the values of counters of other_states' DMAs without lock
-protection is no harm because all DMAs of multi-channels and interrupt
-depend on a master state's DMA, and changing the counters of the master
-state DMA is protected by a spinlock.
-*/
-static int
-ali_write_5_1(struct trident_state *state, const char __user *buf,
- int cnt_for_multi_channel, unsigned int *copy_count,
- unsigned int *state_cnt)
-{
-
- struct dmabuf *dmabuf = &state->dmabuf;
- struct dmabuf *dmabuf_temp;
- const char __user *buffer = buf;
- unsigned swptr, other_dma_nums, sample_s;
- unsigned int i, loop;
-
- other_dma_nums = 4;
- sample_s = sample_size[dmabuf->fmt] >> 1;
- swptr = dmabuf->swptr;
-
- if ((i = state->multi_channels_adjust_count) > 0) {
- if (i == 1) {
- if (copy_from_user(dmabuf->rawbuf + swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
- i--;
- (*state_cnt) += sample_s;
- state->multi_channels_adjust_count++;
- } else
- i = i - (state->chans_num - other_dma_nums);
- for (; (i < other_dma_nums) && (cnt_for_multi_channel > 0); i++) {
- dmabuf_temp = &state->other_states[i]->dmabuf;
- if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
- }
- if (cnt_for_multi_channel == 0)
- state->multi_channels_adjust_count += i;
- }
- if (cnt_for_multi_channel > 0) {
- loop = cnt_for_multi_channel / (state->chans_num * sample_s);
- for (i = 0; i < loop; i++) {
- if (copy_from_user(dmabuf->rawbuf + swptr, buffer,
- sample_s * 2))
- return -EFAULT;
- seek_offset(swptr, buffer, cnt_for_multi_channel,
- sample_s * 2, *copy_count);
- (*state_cnt) += (sample_s * 2);
-
- dmabuf_temp = &state->other_states[0]->dmabuf;
- if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
-
- dmabuf_temp = &state->other_states[1]->dmabuf;
- if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
-
- dmabuf_temp = &state->other_states[2]->dmabuf;
- if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
-
- dmabuf_temp = &state->other_states[3]->dmabuf;
- if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
- }
-
- if (cnt_for_multi_channel > 0) {
- state->multi_channels_adjust_count = cnt_for_multi_channel / sample_s;
-
- if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s))
- return -EFAULT;
- seek_offset(swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
- (*state_cnt) += sample_s;
-
- if (cnt_for_multi_channel > 0) {
- if (copy_from_user(dmabuf->rawbuf + swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(swptr, buffer, cnt_for_multi_channel,
- sample_s, *copy_count);
- (*state_cnt) += sample_s;
-
- if (cnt_for_multi_channel > 0) {
- int diff = state->chans_num - other_dma_nums;
- loop = state->multi_channels_adjust_count - diff;
- for (i = 0; i < loop; i++) {
- dmabuf_temp = &state->other_states[i]->dmabuf;
- if (copy_from_user(dmabuf_temp->rawbuf +
- dmabuf_temp->swptr,
- buffer, sample_s))
- return -EFAULT;
- seek_offset(dmabuf_temp->swptr, buffer,
- cnt_for_multi_channel,
- sample_s, *copy_count);
- }
- }
- }
- } else
- state->multi_channels_adjust_count = 0;
- }
- for (i = 0; i < other_dma_nums; i++) {
- dmabuf_temp = &state->other_states[i]->dmabuf;
- dmabuf_temp->swptr = dmabuf_temp->swptr % dmabuf_temp->dmasize;
- }
- return *state_cnt;
-}
-
-static void
-ali_free_other_states_resources(struct trident_state *state)
-{
- int i;
- struct trident_card *card = state->card;
- struct trident_state *s;
- unsigned other_states_count;
-
- other_states_count = state->chans_num - 2; /* except PCM L/R channels */
- for (i = 0; i < other_states_count; i++) {
- s = state->other_states[i];
- dealloc_dmabuf(&s->dmabuf, card->pci_dev);
- ali_disable_special_channel(s->card, s->dmabuf.channel->num);
- state->card->free_pcm_channel(s->card, s->dmabuf.channel->num);
- card->states[s->virt] = NULL;
- kfree(s);
- }
-}
-
-static struct proc_dir_entry *res;
-
-static int
-ali_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
-{
- struct trident_card *card = (struct trident_card *) data;
- unsigned long flags;
- char c;
-
- if (count < 0)
- return -EINVAL;
- if (count == 0)
- return 0;
- if (get_user(c, buffer))
- return -EFAULT;
-
- spin_lock_irqsave(&card->lock, flags);
- switch (c) {
- case '0':
- ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
- ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL);
- break;
- case '1':
- ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT |
- ALI_SPDIF_OUT_PCM);
- break;
- case '2':
- ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT |
- ALI_SPDIF_OUT_NON_PCM);
- break;
- case '3':
- ali_disable_spdif_in(card); //default
- break;
- case '4':
- ali_setup_spdif_in(card);
- break;
- }
- spin_unlock_irqrestore(&card->lock, flags);
-
- return count;
-}
-
-/* OSS /dev/mixer file operation methods */
-static int
-trident_open_mixdev(struct inode *inode, struct file *file)
-{
- int i = 0;
- int minor = iminor(inode);
- struct trident_card *card = devs;
-
- for (card = devs; card != NULL; card = card->next)
- for (i = 0; i < NR_AC97; i++)
- if (card->ac97_codec[i] != NULL &&
- card->ac97_codec[i]->dev_mixer == minor)
- goto match;
-
- if (!card) {
- return -ENODEV;
- }
- match:
- file->private_data = card->ac97_codec[i];
-
- return nonseekable_open(inode, file);
-}
-
-static int
-trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct ac97_codec *codec = (struct ac97_codec *) file->private_data;
-
- return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static const struct file_operations trident_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .ioctl = trident_ioctl_mixdev,
- .open = trident_open_mixdev,
-};
-
-static int
-ali_reset_5451(struct trident_card *card)
-{
- struct pci_dev *pci_dev = NULL;
- unsigned int dwVal;
- unsigned short wCount, wReg;
-
- pci_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
- pci_dev);
- if (pci_dev == NULL)
- return -1;
-
- pci_read_config_dword(pci_dev, 0x7c, &dwVal);
- pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
- udelay(5000);
- pci_read_config_dword(pci_dev, 0x7c, &dwVal);
- pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
- udelay(5000);
- pci_dev_put(pci_dev);
-
- pci_dev = card->pci_dev;
- if (pci_dev == NULL)
- return -1;
-
- pci_read_config_dword(pci_dev, 0x44, &dwVal);
- pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
- udelay(500);
- pci_read_config_dword(pci_dev, 0x44, &dwVal);
- pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
- udelay(5000);
-
- /* TODO: recognize if we have a PM capable codec and only do this */
- /* if the codec is PM capable */
- wCount = 2000;
- while (wCount--) {
- wReg = ali_ac97_get(card, 0, AC97_POWER_CONTROL);
- if ((wReg & 0x000f) == 0x000f)
- return 0;
- udelay(5000);
- }
- /* This is non fatal if you have a non PM capable codec.. */
- return 0;
-}
-
-/* AC97 codec initialisation. */
-static int __devinit
-trident_ac97_init(struct trident_card *card)
-{
- int num_ac97 = 0;
- unsigned long ready_2nd = 0;
- struct ac97_codec *codec;
- int i = 0;
-
- /* initialize controller side of AC link, and find out if secondary codes
- really exist */
- switch (card->pci_id) {
- case PCI_DEVICE_ID_ALI_5451:
- if (ali_reset_5451(card)) {
- printk(KERN_ERR "trident_ac97_init: error "
- "resetting 5451.\n");
- return -1;
- }
- outl(0x80000001, TRID_REG(card, ALI_GLOBAL_CONTROL));
- outl(0x00000000, TRID_REG(card, T4D_AINTEN_A));
- outl(0xffffffff, TRID_REG(card, T4D_AINT_A));
- outl(0x00000000, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
- outb(0x10, TRID_REG(card, ALI_MPUR2));
- ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
- ready_2nd &= 0x3fff;
- outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL));
- ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
- ready_2nd &= SI_AC97_SECONDARY_READY;
- if (card->revision < ALI_5451_V02)
- ready_2nd = 0;
- break;
- case PCI_DEVICE_ID_SI_7018:
- /* disable AC97 GPIO interrupt */
- outl(0x00, TRID_REG(card, SI_AC97_GPIO));
- /* when power up the AC link is in cold reset mode so stop it */
- outl(PCMOUT | SURROUT | CENTEROUT | LFEOUT | SECONDARY_ID,
- TRID_REG(card, SI_SERIAL_INTF_CTRL));
- /* it take a long time to recover from a cold reset */
- /* (especially when you have more than one codec) */
- udelay(2000);
- ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
- ready_2nd &= SI_AC97_SECONDARY_READY;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- /* playback on */
- outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */
- outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT));
- ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT));
- ready_2nd &= NX_AC97_SECONDARY_READY;
- break;
- case PCI_DEVICE_ID_INTERG_5050:
- /* disable AC97 GPIO interrupt */
- outl(0x00, TRID_REG(card, SI_AC97_GPIO));
- /* when power up, the AC link is in cold reset mode, so stop it */
- outl(PCMOUT | SURROUT | CENTEROUT | LFEOUT,
- TRID_REG(card, SI_SERIAL_INTF_CTRL));
- /* it take a long time to recover from a cold reset (especially */
- /* when you have more than one codec) */
- udelay(2000);
- ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
- ready_2nd &= SI_AC97_SECONDARY_READY;
- break;
- }
-
- for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
- if ((codec = ac97_alloc_codec()) == NULL)
- return -ENOMEM;
-
- /* initialize some basic codec information, other fields */
- /* will be filled in ac97_probe_codec */
- codec->private_data = card;
- codec->id = num_ac97;
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- codec->codec_read = ali_ac97_read;
- codec->codec_write = ali_ac97_write;
- } else {
- codec->codec_read = trident_ac97_get;
- codec->codec_write = trident_ac97_set;
- }
-
- if (ac97_probe_codec(codec) == 0)
- break;
-
- codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1);
- if (codec->dev_mixer < 0) {
- printk(KERN_ERR "trident: couldn't register mixer!\n");
- ac97_release_codec(codec);
- break;
- }
-
- card->ac97_codec[num_ac97] = codec;
-
- /* if there is no secondary codec at all, don't probe any more */
- if (!ready_2nd)
- break;
- }
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
- if (card->ac97_codec[num_ac97] == NULL)
- break;
- for (i = 0; i < 64; i++) {
- u16 reg = ali_ac97_get(card, num_ac97, i * 2);
- card->mixer_regs[i][num_ac97] = reg;
- }
- }
- }
- return num_ac97 + 1;
-}
-
-#ifdef SUPPORT_JOYSTICK
-/* Gameport functions for the cards ADC gameport */
-
-static unsigned char trident_game_read(struct gameport *gameport)
-{
- struct trident_card *card = gameport->port_data;
-
- return inb(TRID_REG(card, T4D_GAME_LEG));
-}
-
-static void trident_game_trigger(struct gameport *gameport)
-{
- struct trident_card *card = gameport->port_data;
-
- outb(0xff, TRID_REG(card, T4D_GAME_LEG));
-}
-
-static int trident_game_cooked_read(struct gameport *gameport,
- int *axes, int *buttons)
-{
- struct trident_card *card = gameport->port_data;
- int i;
-
- *buttons = (~inb(TRID_REG(card, T4D_GAME_LEG)) >> 4) & 0xf;
-
- for (i = 0; i < 4; i++) {
- axes[i] = inw(TRID_REG(card, T4D_GAME_AXD) + i * sizeof (u16));
- if (axes[i] == 0xffff)
- axes[i] = -1;
- }
-
- return 0;
-}
-
-static int trident_game_open(struct gameport *gameport, int mode)
-{
- struct trident_card *card = gameport->port_data;
-
- switch (mode) {
- case GAMEPORT_MODE_COOKED:
- outb(0x80, TRID_REG(card, T4D_GAME_CR));
- msleep(20);
- return 0;
- case GAMEPORT_MODE_RAW:
- outb(0x00, TRID_REG(card, T4D_GAME_CR));
- return 0;
- default:
- return -1;
- }
-
- return 0;
-}
-
-static int __devinit trident_register_gameport(struct trident_card *card)
-{
- struct gameport *gp;
-
- card->gameport = gp = gameport_allocate_port();
- if (!gp) {
- printk(KERN_ERR "trident: can not allocate memory for gameport\n");
- return -ENOMEM;
- }
-
- gameport_set_name(gp, "Trident 4DWave");
- gameport_set_phys(gp, "pci%s/gameport0", pci_name(card->pci_dev));
- gp->read = trident_game_read;
- gp->trigger = trident_game_trigger;
- gp->cooked_read = trident_game_cooked_read;
- gp->open = trident_game_open;
- gp->fuzz = 64;
- gp->port_data = card;
-
- gameport_register_port(gp);
-
- return 0;
-}
-
-static inline void trident_unregister_gameport(struct trident_card *card)
-{
- if (card->gameport)
- gameport_unregister_port(card->gameport);
-}
-
-#else
-static inline int trident_register_gameport(struct trident_card *card) { return -ENOSYS; }
-static inline void trident_unregister_gameport(struct trident_card *card) { }
-#endif /* SUPPORT_JOYSTICK */
-
-/* install the driver, we do not allocate hardware channel nor DMA buffer */
-/* now, they are defered until "ACCESS" time (in prog_dmabuf called by */
-/* open/read/write/ioctl/mmap) */
-static int __devinit
-trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
-{
- unsigned long iobase;
- struct trident_card *card;
- u8 bits;
- u8 revision;
- int i = 0;
- u16 temp;
- struct pci_dev *pci_dev_m1533 = NULL;
- int rc = -ENODEV;
- u64 dma_mask;
-
- if (pci_enable_device(pci_dev))
- goto out;
-
- if (pci_dev->device == PCI_DEVICE_ID_ALI_5451)
- dma_mask = ALI_DMA_MASK;
- else
- dma_mask = TRIDENT_DMA_MASK;
- if (pci_set_dma_mask(pci_dev, dma_mask)) {
- printk(KERN_ERR "trident: architecture does not support"
- " %s PCI busmaster DMA\n",
- pci_dev->device == PCI_DEVICE_ID_ALI_5451 ?
- "32-bit" : "30-bit");
- goto out;
- }
- pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
-
- if (pci_id->device == PCI_DEVICE_ID_INTERG_5050)
- iobase = pci_resource_start(pci_dev, 1);
- else
- iobase = pci_resource_start(pci_dev, 0);
-
- if (!request_region(iobase, 256, card_names[pci_id->driver_data])) {
- printk(KERN_ERR "trident: can't allocate I/O space at "
- "0x%4.4lx\n", iobase);
- goto out;
- }
-
- rc = -ENOMEM;
- if ((card = kzalloc(sizeof(*card), GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "trident: out of memory\n");
- goto out_release_region;
- }
-
- init_timer(&card->timer);
- card->iobase = iobase;
- card->pci_dev = pci_dev_get(pci_dev);
- card->pci_id = pci_id->device;
- card->revision = revision;
- card->irq = pci_dev->irq;
- card->next = devs;
- card->magic = TRIDENT_CARD_MAGIC;
- card->banks[BANK_A].addresses = &bank_a_addrs;
- card->banks[BANK_A].bitmap = 0UL;
- card->banks[BANK_B].addresses = &bank_b_addrs;
- card->banks[BANK_B].bitmap = 0UL;
-
- mutex_init(&card->open_mutex);
- spin_lock_init(&card->lock);
- init_timer(&card->timer);
-
- devs = card;
-
- pci_set_master(pci_dev);
-
- printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n",
- card_names[pci_id->driver_data], card->iobase, card->irq);
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- /* ALi channel Management */
- card->alloc_pcm_channel = ali_alloc_pcm_channel;
- card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel;
- card->free_pcm_channel = ali_free_pcm_channel;
-
- card->address_interrupt = ali_address_interrupt;
-
- /* Added by Matt Wu 01-05-2001 for spdif in */
- card->multi_channel_use_count = 0;
- card->rec_channel_use_count = 0;
-
- /* ALi SPDIF OUT function */
- if (card->revision == ALI_5451_V02) {
- ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
- res = create_proc_entry("ALi5451", 0, NULL);
- if (res) {
- res->write_proc = ali_write_proc;
- res->data = card;
- }
- }
-
- /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */
- card->hwvolctl = 0;
- pci_dev_m1533 = pci_get_device(PCI_VENDOR_ID_AL,
- PCI_DEVICE_ID_AL_M1533,
- pci_dev_m1533);
- rc = -ENODEV;
- if (pci_dev_m1533 == NULL)
- goto out_proc_fs;
- pci_read_config_byte(pci_dev_m1533, 0x63, &bits);
- if (bits & (1 << 5))
- card->hwvolctl = 1;
- if (card->hwvolctl) {
- /* Clear m1533 pci cfg 78h bit 30 to zero, which makes
- GPIO11/12/13 work as ACGP_UP/DOWN/MUTE. */
- pci_read_config_byte(pci_dev_m1533, 0x7b, &bits);
- bits &= 0xbf; /*clear bit 6 */
- pci_write_config_byte(pci_dev_m1533, 0x7b, bits);
- }
- pci_dev_put(pci_dev_m1533);
-
- } else if (card->pci_id == PCI_DEVICE_ID_INTERG_5050) {
- card->alloc_pcm_channel = cyber_alloc_pcm_channel;
- card->alloc_rec_pcm_channel = cyber_alloc_pcm_channel;
- card->free_pcm_channel = cyber_free_pcm_channel;
- card->address_interrupt = cyber_address_interrupt;
- cyber_init_ritual(card);
- } else {
- card->alloc_pcm_channel = trident_alloc_pcm_channel;
- card->alloc_rec_pcm_channel = trident_alloc_pcm_channel;
- card->free_pcm_channel = trident_free_pcm_channel;
- card->address_interrupt = trident_address_interrupt;
- }
-
- /* claim our irq */
- rc = -ENODEV;
- if (request_irq(card->irq, &trident_interrupt, IRQF_SHARED,
- card_names[pci_id->driver_data], card)) {
- printk(KERN_ERR "trident: unable to allocate irq %d\n",
- card->irq);
- goto out_proc_fs;
- }
- /* register /dev/dsp */
- if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) {
- printk(KERN_ERR "trident: couldn't register DSP device!\n");
- goto out_free_irq;
- }
- card->mixer_regs_ready = 0;
- /* initialize AC97 codec and register /dev/mixer */
- if (trident_ac97_init(card) <= 0) {
- /* unregister audio devices */
- for (i = 0; i < NR_AC97; i++) {
- if (card->ac97_codec[i] != NULL) {
- struct ac97_codec* codec = card->ac97_codec[i];
- unregister_sound_mixer(codec->dev_mixer);
- ac97_release_codec(codec);
- }
- }
- goto out_unregister_sound_dsp;
- }
- card->mixer_regs_ready = 1;
- outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
-
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */
- if (card->hwvolctl) {
- /* Enable GPIO IRQ (MISCINT bit 18h) */
- temp = inw(TRID_REG(card, T4D_MISCINT + 2));
- temp |= 0x0004;
- outw(temp, TRID_REG(card, T4D_MISCINT + 2));
-
- /* Enable H/W Volume Control GLOVAL CONTROL bit 0 */
- temp = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
- temp |= 0x0001;
- outw(temp, TRID_REG(card, ALI_GLOBAL_CONTROL));
-
- }
- if (card->revision == ALI_5451_V02)
- ali_close_multi_channels();
- /* edited by HMSEO for GT sound */
-#if defined(CONFIG_ALPHA_NAUTILUS) || defined(CONFIG_ALPHA_GENERIC)
- {
- u16 ac97_data;
- extern struct hwrpb_struct *hwrpb;
-
- if ((hwrpb->sys_type) == 201) {
- printk(KERN_INFO "trident: Running on Alpha system "
- "type Nautilus\n");
- ac97_data = ali_ac97_get(card, 0, AC97_POWER_CONTROL);
- ali_ac97_set(card, 0, AC97_POWER_CONTROL,
- ac97_data | ALI_EAPD_POWER_DOWN);
- }
- }
-#endif /* CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC */
- /* edited by HMSEO for GT sound */
- }
- rc = 0;
- pci_set_drvdata(pci_dev, card);
-
- /* Enable Address Engine Interrupts */
- trident_enable_loop_interrupts(card);
-
- /* Register gameport */
- trident_register_gameport(card);
-
-out:
- return rc;
-
-out_unregister_sound_dsp:
- unregister_sound_dsp(card->dev_audio);
-out_free_irq:
- free_irq(card->irq, card);
-out_proc_fs:
- pci_dev_put(card->pci_dev);
- if (res) {
- remove_proc_entry("ALi5451", NULL);
- res = NULL;
- }
- kfree(card);
- devs = NULL;
-out_release_region:
- release_region(iobase, 256);
- return rc;
-}
-
-static void __devexit
-trident_remove(struct pci_dev *pci_dev)
-{
- int i;
- struct trident_card *card = pci_get_drvdata(pci_dev);
-
- /*
- * Kill running timers before unload. We can't have them
- * going off after rmmod!
- */
- if (card->hwvolctl)
- del_timer_sync(&card->timer);
-
- /* ALi S/PDIF and Power Management */
- if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
- ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
- ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL);
- ali_disable_spdif_in(card);
- remove_proc_entry("ALi5451", NULL);
- }
-
- /* Unregister gameport */
- trident_unregister_gameport(card);
-
- /* Kill interrupts, and SP/DIF */
- trident_disable_loop_interrupts(card);
-
- /* free hardware resources */
- free_irq(card->irq, card);
- release_region(card->iobase, 256);
-
- /* unregister audio devices */
- for (i = 0; i < NR_AC97; i++)
- if (card->ac97_codec[i] != NULL) {
- unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
- ac97_release_codec(card->ac97_codec[i]);
- }
- unregister_sound_dsp(card->dev_audio);
-
- pci_set_drvdata(pci_dev, NULL);
- pci_dev_put(card->pci_dev);
- kfree(card);
-}
-
-MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho, Ching Ling Lee, Muli Ben-Yehuda");
-MODULE_DESCRIPTION("Trident 4DWave/SiS 7018/ALi 5451 and Tvia/IGST CyberPro5050 PCI "
- "Audio Driver");
-MODULE_LICENSE("GPL");
-
-#define TRIDENT_MODULE_NAME "trident"
-
-static struct pci_driver trident_pci_driver = {
- .name = TRIDENT_MODULE_NAME,
- .id_table = trident_pci_tbl,
- .probe = trident_probe,
- .remove = __devexit_p(trident_remove),
-#ifdef CONFIG_PM
- .suspend = trident_suspend,
- .resume = trident_resume
-#endif
-};
-
-static int __init
-trident_init_module(void)
-{
- printk(KERN_INFO "Trident 4DWave/SiS 7018/ALi 5451,Tvia CyberPro "
- "5050 PCI Audio, version " DRIVER_VERSION ", " __TIME__ " "
- __DATE__ "\n");
-
- return pci_register_driver(&trident_pci_driver);
-}
-
-static void __exit
-trident_cleanup_module(void)
-{
- pci_unregister_driver(&trident_pci_driver);
-}
-
-module_init(trident_init_module);
-module_exit(trident_cleanup_module);
diff --git a/sound/oss/trident.h b/sound/oss/trident.h
deleted file mode 100644
index ff30a1d7c2f..00000000000
--- a/sound/oss/trident.h
+++ /dev/null
@@ -1,358 +0,0 @@
-#ifndef __TRID4DWAVE_H
-#define __TRID4DWAVE_H
-
-/*
- * audio@tridentmicro.com
- * Fri Feb 19 15:55:28 MST 1999
- * Definitions for Trident 4DWave DX/NX chips
- *
- *
- * 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.
- *
- */
-
-/* PCI vendor and device ID */
-#ifndef PCI_VENDOR_ID_TRIDENT
-#define PCI_VENDOR_ID_TRIDENT 0x1023
-#endif
-
-#ifndef PCI_VENDOR_ID_SI
-#define PCI_VENDOR_ID_SI 0x1039
-#endif
-
-#ifndef PCI_VENDOR_ID_ALI
-#define PCI_VENDOR_ID_ALI 0x10b9
-#endif
-
-#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX
-#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000
-#endif
-
-#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX
-#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001
-#endif
-
-#ifndef PCI_DEVICE_ID_SI_7018
-#define PCI_DEVICE_ID_SI_7018 0x7018
-#endif
-
-#ifndef PCI_DEVICE_ID_ALI_5451
-#define PCI_DEVICE_ID_ALI_5451 0x5451
-#endif
-
-#ifndef PCI_DEVICE_ID_ALI_1533
-#define PCI_DEVICE_ID_ALI_1533 0x1533
-#endif
-
-#define CHANNEL_REGS 5
-#define CHANNEL_START 0xe0 // The first bytes of the contiguous register space.
-
-#define BANK_A 0
-#define BANK_B 1
-#define NR_BANKS 2
-
-#define TRIDENT_FMT_STEREO 0x01
-#define TRIDENT_FMT_16BIT 0x02
-#define TRIDENT_FMT_MASK 0x03
-
-#define DAC_RUNNING 0x01
-#define ADC_RUNNING 0x02
-
-/* Register Addresses */
-
-/* operational registers common to DX, NX, 7018 */
-enum trident_op_registers {
- T4D_GAME_CR = 0x30, T4D_GAME_LEG = 0x31,
- T4D_GAME_AXD = 0x34,
- T4D_REC_CH = 0x70,
- T4D_START_A = 0x80, T4D_STOP_A = 0x84,
- T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c,
- T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94,
- T4D_AINT_A = 0x98, T4D_EINT_A = 0x9c,
- T4D_LFO_GC_CIR = 0xa0, T4D_AINTEN_A = 0xa4,
- T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac,
- T4D_MISCINT = 0xb0, T4D_START_B = 0xb4,
- T4D_STOP_B = 0xb8, T4D_CSPF_B = 0xbc,
- T4D_SBBL_SBCL = 0xc0, T4D_SBCTRL_SBE2R_SBDD = 0xc4,
- T4D_STIMER = 0xc8, T4D_LFO_B_I2S_DELTA = 0xcc,
- T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc,
- ALI_MPUR2 = 0x22, ALI_GPIO = 0x7c,
- ALI_EBUF1 = 0xf4,
- ALI_EBUF2 = 0xf8
-};
-
-enum ali_op_registers {
- ALI_SCTRL = 0x48,
- ALI_GLOBAL_CONTROL = 0xd4,
- ALI_STIMER = 0xc8,
- ALI_SPDIF_CS = 0x70,
- ALI_SPDIF_CTRL = 0x74
-};
-
-enum ali_registers_number {
- ALI_GLOBAL_REGS = 56,
- ALI_CHANNEL_REGS = 8,
- ALI_MIXER_REGS = 20
-};
-
-enum ali_sctrl_control_bit {
- ALI_SPDIF_OUT_ENABLE = 0x20
-};
-
-enum ali_global_control_bit {
- ALI_SPDIF_OUT_SEL_PCM = 0x00000400,
- ALI_SPDIF_IN_SUPPORT = 0x00000800,
- ALI_SPDIF_OUT_CH_ENABLE = 0x00008000,
- ALI_SPDIF_IN_CH_ENABLE = 0x00080000,
- ALI_PCM_IN_DISABLE = 0x7fffffff,
- ALI_PCM_IN_ENABLE = 0x80000000,
- ALI_SPDIF_IN_CH_DISABLE = 0xfff7ffff,
- ALI_SPDIF_OUT_CH_DISABLE = 0xffff7fff,
- ALI_SPDIF_OUT_SEL_SPDIF = 0xfffffbff
-
-};
-
-enum ali_spdif_control_bit {
- ALI_SPDIF_IN_FUNC_ENABLE = 0x02,
- ALI_SPDIF_IN_CH_STATUS = 0x40,
- ALI_SPDIF_OUT_CH_STATUS = 0xbf
-
-};
-
-enum ali_control_all {
- ALI_DISABLE_ALL_IRQ = 0,
- ALI_CHANNELS = 32,
- ALI_STOP_ALL_CHANNELS = 0xffffffff,
- ALI_MULTI_CHANNELS_START_STOP = 0x07800000
-};
-
-enum ali_EMOD_control_bit {
- ALI_EMOD_DEC = 0x00000000,
- ALI_EMOD_INC = 0x10000000,
- ALI_EMOD_Delay = 0x20000000,
- ALI_EMOD_Still = 0x30000000
-};
-
-enum ali_pcm_in_channel_num {
- ALI_NORMAL_CHANNEL = 0,
- ALI_SPDIF_OUT_CHANNEL = 15,
- ALI_SPDIF_IN_CHANNEL = 19,
- ALI_LEF_CHANNEL = 23,
- ALI_CENTER_CHANNEL = 24,
- ALI_SURR_RIGHT_CHANNEL = 25,
- ALI_SURR_LEFT_CHANNEL = 26,
- ALI_PCM_IN_CHANNEL = 31
-};
-
-enum ali_pcm_out_channel_num {
- ALI_PCM_OUT_CHANNEL_FIRST = 0,
- ALI_PCM_OUT_CHANNEL_LAST = 31
-};
-
-enum ali_ac97_power_control_bit {
- ALI_EAPD_POWER_DOWN = 0x8000
-};
-
-enum ali_update_ptr_flags {
- ALI_ADDRESS_INT_UPDATE = 0x01
-};
-
-enum ali_revision {
- ALI_5451_V02 = 0x02
-};
-
-enum ali_spdif_out_control {
- ALI_PCM_TO_SPDIF_OUT = 0,
- ALI_SPDIF_OUT_TO_SPDIF_OUT = 1,
- ALI_SPDIF_OUT_PCM = 0,
- ALI_SPDIF_OUT_NON_PCM = 2
-};
-
-/* S/PDIF Operational Registers for 4D-NX */
-enum nx_spdif_registers {
- NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28,
- NX_SPESO = 0x2c, NX_SPCSTATUS = 0x64
-};
-
-/* OP registers to access each hardware channel */
-enum channel_registers {
- CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8,
- CH_DX_FMC_RVOL_CVOL = 0xec,
- CH_NX_DELTA_CSO = 0xe0, CH_NX_DELTA_ESO = 0xe8,
- CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec,
- CH_LBA = 0xe4,
- CH_GVSEL_PAN_VOL_CTRL_EC = 0xf0
-};
-
-/* registers to read/write/control AC97 codec */
-enum dx_ac97_registers {
- DX_ACR0_AC97_W = 0x40, DX_ACR1_AC97_R = 0x44,
- DX_ACR2_AC97_COM_STAT = 0x48
-};
-
-enum nx_ac97_registers {
- NX_ACR0_AC97_COM_STAT = 0x40, NX_ACR1_AC97_W = 0x44,
- NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY = 0x4c
-};
-
-enum si_ac97_registers {
- SI_AC97_WRITE = 0x40, SI_AC97_READ = 0x44,
- SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c
-};
-
-enum ali_ac97_registers {
- ALI_AC97_WRITE = 0x40, ALI_AC97_READ = 0x44
-};
-
-/* Bit mask for operational registers */
-#define AC97_REG_ADDR 0x000000ff
-
-enum ali_ac97_bits {
- ALI_AC97_BUSY_WRITE = 0x8000, ALI_AC97_BUSY_READ = 0x8000,
- ALI_AC97_WRITE_ACTION = 0x8000, ALI_AC97_READ_ACTION = 0x8000,
- ALI_AC97_AUDIO_BUSY = 0x4000, ALI_AC97_SECONDARY = 0x0080,
- ALI_AC97_READ_MIXER_REGISTER = 0xfeff,
- ALI_AC97_WRITE_MIXER_REGISTER = 0x0100
-};
-
-enum sis7018_ac97_bits {
- SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000,
- SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000,
- SI_AC97_SECONDARY = 0x0080
-};
-
-enum trident_dx_ac97_bits {
- DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000,
- DX_AC97_READY = 0x0010, DX_AC97_RECORD = 0x0008,
- DX_AC97_PLAYBACK = 0x0002
-};
-
-enum trident_nx_ac97_bits {
- /* ACR1-3 */
- NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800,
- NX_AC97_BUSY_DATA = 0x0400, NX_AC97_WRITE_SECONDARY = 0x0100,
- /* ACR0 */
- NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020,
- NX_AC97_SURROUND_OUTPUT = 0x0010,
- NX_AC97_PRIMARY_READY = 0x0008, NX_AC97_PRIMARY_RECORD = 0x0004,
- NX_AC97_PCM_OUTPUT = 0x0002,
- NX_AC97_WARM_RESET = 0x0001
-};
-
-enum serial_intf_ctrl_bits {
- WARM_REST = 0x00000001, COLD_RESET = 0x00000002,
- I2S_CLOCK = 0x00000004, PCM_SEC_AC97= 0x00000008,
- AC97_DBL_RATE = 0x00000010, SPDIF_EN = 0x00000020,
- I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080,
- PCMIN = 0x00000100, LINE1IN = 0x00000200,
- MICIN = 0x00000400, LINE2IN = 0x00000800,
- HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000,
- /* 7018 spec says id = 01 but the demo board routed to 10
- SECONDARY_ID= 0x00004000, */
- SECONDARY_ID= 0x00004000,
- PCMOUT = 0x00010000, SURROUT = 0x00020000,
- CENTEROUT = 0x00040000, LFEOUT = 0x00080000,
- LINE1OUT = 0x00100000, LINE2OUT = 0x00200000,
- GPIOOUT = 0x00400000,
- SI_AC97_PRIMARY_READY = 0x01000000,
- SI_AC97_SECONDARY_READY = 0x02000000,
-};
-
-enum global_control_bits {
- CHANNLE_IDX = 0x0000003f, PB_RESET = 0x00000100,
- PAUSE_ENG = 0x00000200,
- OVERRUN_IE = 0x00000400, UNDERRUN_IE = 0x00000800,
- ENDLP_IE = 0x00001000, MIDLP_IE = 0x00002000,
- ETOG_IE = 0x00004000,
- EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000
-};
-
-enum channel_control_bits {
- CHANNEL_LOOP = 0x00001000, CHANNEL_SIGNED = 0x00002000,
- CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000,
-};
-
-enum channel_attribute {
- /* playback/record select */
- CHANNEL_PB = 0x0000, CHANNEL_SPC_PB = 0x4000,
- CHANNEL_REC = 0x8000, CHANNEL_REC_PB = 0xc000,
- /* playback destination/record source select */
- MODEM_LINE1 = 0x0000, MODEM_LINE2 = 0x0400,
- PCM_LR = 0x0800, HSET = 0x0c00,
- I2S_LR = 0x1000, CENTER_LFE = 0x1400,
- SURR_LR = 0x1800, SPDIF_LR = 0x1c00,
- MIC = 0x1400,
- /* mist stuff */
- MONO_LEFT = 0x0000, MONO_RIGHT = 0x0100,
- MONO_MIX = 0x0200, SRC_ENABLE = 0x0080,
-};
-
-enum miscint_bits {
- PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002,
- SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008,
- OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020,
- ENVELOPE_IRQ = 0x00000040, ST_IRQ = 0x00000080,
- PB_UNDERRUN = 0x00000100, REC_OVERRUN = 0x00000200,
- MIXER_UNDERFLOW = 0x00000400, MIXER_OVERFLOW = 0x00000800,
- ST_TARGET_REACHED = 0x00008000, PB_24K_MODE = 0x00010000,
- ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000
-};
-
-#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) )
-
-#define CYBER_PORT_AUDIO 0x3CE
-#define CYBER_IDX_AUDIO_ENABLE 0x7B
-#define CYBER_BMSK_AUDIO_INT_ENABLE 0x09
-#define CYBER_BMSK_AUENZ 0x01
-#define CYBER_BMSK_AUENZ_ENABLE 0x00
-#define CYBER_IDX_IRQ_ENABLE 0x12
-
-#define VALIDATE_MAGIC(FOO,MAG) \
-({ \
- if (!(FOO) || (FOO)->magic != MAG) { \
- printk(invalid_magic,__func__); \
- return -ENXIO; \
- } \
-})
-
-#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC)
-#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC)
-
-static inline unsigned ld2(unsigned int x)
-{
- unsigned r = 0;
-
- if (x >= 0x10000) {
- x >>= 16;
- r += 16;
- }
- if (x >= 0x100) {
- x >>= 8;
- r += 8;
- }
- if (x >= 0x10) {
- x >>= 4;
- r += 4;
- }
- if (x >= 4) {
- x >>= 2;
- r += 2;
- }
- if (x >= 2)
- r++;
- return r;
-}
-
-#endif /* __TRID4DWAVE_H */
diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c
index bb4a0969f46..725fef0f59a 100644
--- a/sound/oss/vidc.c
+++ b/sound/oss/vidc.c
@@ -22,7 +22,7 @@
#include <linux/kernel.h>
#include <linux/interrupt.h>
-#include <asm/hardware.h>
+#include <mach/hardware.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/hardware/iomd.h>
diff --git a/sound/oss/vidc_fill.S b/sound/oss/vidc_fill.S
index 01ccc074cc1..bed34921d17 100644
--- a/sound/oss/vidc_fill.S
+++ b/sound/oss/vidc_fill.S
@@ -11,7 +11,7 @@
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
-#include <asm/hardware.h>
+#include <mach/hardware.h>
#include <asm/hardware/iomd.h>
.text
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
index 2c5aaa58046..dcbb3f739e6 100644
--- a/sound/oss/vwsnd.c
+++ b/sound/oss/vwsnd.c
@@ -150,7 +150,7 @@
#include <linux/interrupt.h>
#include <linux/mutex.h>
-#include <asm/mach-visws/cobalt.h>
+#include <asm/visws/cobalt.h>
#include "sound_config.h"
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
index 88490418f93..c47842fad65 100644
--- a/sound/oss/waveartist.c
+++ b/sound/oss/waveartist.c
@@ -47,7 +47,7 @@
#include "waveartist.h"
#ifdef CONFIG_ARM
-#include <asm/hardware.h>
+#include <mach/hardware.h>
#include <asm/mach-types.h>
#endif
diff --git a/sound/parisc/Kconfig b/sound/parisc/Kconfig
index a5a7f9d75d0..9b61d95010f 100644
--- a/sound/parisc/Kconfig
+++ b/sound/parisc/Kconfig
@@ -1,15 +1,20 @@
# ALSA PA-RISC drivers
-menu "GSC devices"
- depends on SND!=n && GSC
+menuconfig SND_GSC
+ bool "GSC sound devices"
+ depends on GSC
+ default y
+ help
+ Support for GSC sound devices on PA-RISC architectures.
+
+if SND_GSC
config SND_HARMONY
tristate "Harmony/Vivace sound chip"
- depends on SND
select SND_PCM
help
Say 'Y' or 'M' to include support for the Harmony/Vivace sound
chip found in most GSC-based PA-RISC workstations. It's frequently
provided as part of the Lasi multi-function IC.
-endmenu
+endif # SND_GSC
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index 99f5483abf2..41f870f8a11 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -868,7 +868,8 @@ snd_harmony_mixer_init(struct snd_harmony *h)
struct snd_card *card = h->card;
int idx, err;
- snd_assert(h != NULL, return -EINVAL);
+ if (snd_BUG_ON(!h))
+ return -EINVAL;
strcpy(card->mixername, "Harmony Gain control interface");
for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 7e474210957..7003711f4fc 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -1,11 +1,16 @@
# ALSA PCI drivers
-menu "PCI devices"
- depends on SND!=n && PCI
+menuconfig SND_PCI
+ bool "PCI sound devices"
+ depends on PCI
+ default y
+ help
+ Support for sound devices connected via the PCI bus.
+
+if SND_PCI
config SND_AD1889
tristate "Analog Devices AD1889"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated AC97 sound
@@ -17,7 +22,6 @@ config SND_AD1889
config SND_ALS300
tristate "Avance Logic ALS300/ALS300+"
- depends on SND
select SND_PCM
select SND_AC97_CODEC
select SND_OPL3_LIB
@@ -29,7 +33,7 @@ config SND_ALS300
config SND_ALS4000
tristate "Avance Logic ALS4000"
- depends on SND && ISA_DMA_API
+ depends on ISA_DMA_API
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -43,7 +47,6 @@ config SND_ALS4000
config SND_ALI5451
tristate "ALi M5451 PCI Audio Controller"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -57,7 +60,6 @@ config SND_ALI5451
config SND_ATIIXP
tristate "ATI IXP AC97 Controller"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated AC97 sound
@@ -69,7 +71,6 @@ config SND_ATIIXP
config SND_ATIIXP_MODEM
tristate "ATI IXP Modem"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated MC97 modem on
@@ -80,7 +81,6 @@ config SND_ATIIXP_MODEM
config SND_AU8810
tristate "Aureal Advantage"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -95,7 +95,6 @@ config SND_AU8810
config SND_AU8820
tristate "Aureal Vortex"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -109,7 +108,6 @@ config SND_AU8820
config SND_AU8830
tristate "Aureal Vortex 2"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -124,7 +122,6 @@ config SND_AU8830
config SND_AW2
tristate "Emagic Audiowerk 2"
- depends on SND
help
Say Y here to include support for Emagic Audiowerk 2 soundcards.
@@ -139,7 +136,7 @@ config SND_AW2
config SND_AZT3328
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
- depends on SND && EXPERIMENTAL
+ depends on EXPERIMENTAL
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -152,7 +149,6 @@ config SND_AZT3328
config SND_BT87X
tristate "Bt87x Audio Capture"
- depends on SND
select SND_PCM
help
If you want to record audio from TV cards based on
@@ -174,7 +170,6 @@ config SND_BT87X_OVERCLOCK
config SND_CA0106
tristate "SB Audigy LS / Live 24bit"
- depends on SND
select SND_AC97_CODEC
select SND_RAWMIDI
select SND_VMASTER
@@ -187,7 +182,6 @@ config SND_CA0106
config SND_CMIPCI
tristate "C-Media 8338, 8738, 8768, 8770"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -201,13 +195,11 @@ config 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
@@ -225,7 +217,6 @@ config SND_OXYGEN
config SND_CS4281
tristate "Cirrus Logic (Sound Fusion) CS4281"
- depends on SND
select SND_OPL3_LIB
select SND_RAWMIDI
select SND_AC97_CODEC
@@ -237,7 +228,6 @@ config SND_CS4281
config SND_CS46XX
tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
- depends on SND
select SND_RAWMIDI
select SND_AC97_CODEC
help
@@ -258,7 +248,7 @@ config SND_CS46XX_NEW_DSP
config SND_CS5530
tristate "CS5530 Audio"
- depends on SND && ISA_DMA_API
+ depends on ISA_DMA_API
select SND_SB16_DSP
help
Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
@@ -268,7 +258,7 @@ config SND_CS5530
config SND_CS5535AUDIO
tristate "CS5535/CS5536 Audio"
- depends on SND && X86 && !X86_64
+ depends on X86 && !X86_64
select SND_PCM
select SND_AC97_CODEC
help
@@ -286,7 +276,6 @@ config SND_CS5535AUDIO
config SND_DARLA20
tristate "(Echoaudio) Darla20"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -297,7 +286,6 @@ config SND_DARLA20
config SND_GINA20
tristate "(Echoaudio) Gina20"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -308,7 +296,6 @@ config SND_GINA20
config SND_LAYLA20
tristate "(Echoaudio) Layla20"
- depends on SND
select FW_LOADER
select SND_RAWMIDI
select SND_PCM
@@ -320,7 +307,6 @@ config SND_LAYLA20
config SND_DARLA24
tristate "(Echoaudio) Darla24"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -331,7 +317,6 @@ config SND_DARLA24
config SND_GINA24
tristate "(Echoaudio) Gina24"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -342,7 +327,6 @@ config SND_GINA24
config SND_LAYLA24
tristate "(Echoaudio) Layla24"
- depends on SND
select FW_LOADER
select SND_RAWMIDI
select SND_PCM
@@ -354,7 +338,6 @@ config SND_LAYLA24
config SND_MONA
tristate "(Echoaudio) Mona"
- depends on SND
select FW_LOADER
select SND_RAWMIDI
select SND_PCM
@@ -366,7 +349,6 @@ config SND_MONA
config SND_MIA
tristate "(Echoaudio) Mia"
- depends on SND
select FW_LOADER
select SND_RAWMIDI
select SND_PCM
@@ -378,7 +360,6 @@ config SND_MIA
config SND_ECHO3G
tristate "(Echoaudio) 3G cards"
- depends on SND
select FW_LOADER
select SND_RAWMIDI
select SND_PCM
@@ -390,7 +371,6 @@ config SND_ECHO3G
config SND_INDIGO
tristate "(Echoaudio) Indigo"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -401,7 +381,6 @@ config SND_INDIGO
config SND_INDIGOIO
tristate "(Echoaudio) Indigo IO"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -412,7 +391,6 @@ config SND_INDIGOIO
config SND_INDIGODJ
tristate "(Echoaudio) Indigo DJ"
- depends on SND
select FW_LOADER
select SND_PCM
help
@@ -423,7 +401,6 @@ config SND_INDIGODJ
config SND_EMU10K1
tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
- depends on SND
select FW_LOADER
select SND_HWDEP
select SND_RAWMIDI
@@ -441,7 +418,6 @@ config SND_EMU10K1
config SND_EMU10K1X
tristate "Emu10k1X (Dell OEM Version)"
- depends on SND
select SND_AC97_CODEC
select SND_RAWMIDI
help
@@ -453,7 +429,6 @@ config SND_EMU10K1X
config SND_ENS1370
tristate "(Creative) Ensoniq AudioPCI 1370"
- depends on SND
select SND_RAWMIDI
select SND_PCM
help
@@ -464,7 +439,6 @@ config SND_ENS1370
config SND_ENS1371
tristate "(Creative) Ensoniq AudioPCI 1371/1373"
- depends on SND
select SND_RAWMIDI
select SND_AC97_CODEC
help
@@ -476,7 +450,6 @@ config SND_ENS1371
config SND_ES1938
tristate "ESS ES1938/1946/1969 (Solo-1)"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -489,7 +462,6 @@ config SND_ES1938
config SND_ES1968
tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -501,7 +473,6 @@ config SND_ES1968
config SND_FM801
tristate "ForteMedia FM801"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -528,7 +499,6 @@ config SND_FM801_TEA575X
config SND_HDA_INTEL
tristate "Intel HD Audio"
- depends on SND
select SND_PCM
select SND_VMASTER
help
@@ -547,6 +517,14 @@ config SND_HDA_HWDEP
This interface can be used for out-of-band communication
with codecs for debugging purposes.
+config SND_HDA_INPUT_BEEP
+ bool "Support digital beep via input layer"
+ depends on SND_HDA_INTEL
+ depends on INPUT=y || INPUT=SND_HDA_INTEL
+ help
+ Say Y here to build a digital beep interface for HD-audio
+ driver. This interface is used to generate digital beeps.
+
config SND_HDA_CODEC_REALTEK
bool "Build Realtek HD-audio codec support"
depends on SND_HDA_INTEL
@@ -587,6 +565,14 @@ config SND_HDA_CODEC_ATIHDMI
Say Y here to include ATI HDMI HD-audio codec support in
snd-hda-intel driver, such as ATI RS600 HDMI.
+config SND_HDA_CODEC_NVHDMI
+ bool "Build NVIDIA HDMI HD-audio codec support"
+ depends on SND_HDA_INTEL
+ default y
+ help
+ Say Y here to include NVIDIA HDMI HD-audio codec support in
+ snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
+
config SND_HDA_CODEC_CONEXANT
bool "Build Conexant HD-audio codec support"
depends on SND_HDA_INTEL
@@ -637,7 +623,6 @@ config SND_HDA_POWER_SAVE_DEFAULT
config SND_HDSP
tristate "RME Hammerfall DSP Audio"
- depends on SND
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
@@ -650,7 +635,6 @@ config SND_HDSP
config SND_HDSPM
tristate "RME Hammerfall DSP MADI"
- depends on SND
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
@@ -663,7 +647,6 @@ config 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
@@ -674,7 +657,6 @@ config SND_HIFIER
config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -683,16 +665,16 @@ config SND_ICE1712
Currently supported hardware is: M-Audio Delta 1010(LT),
DiO 2496, 66, 44, 410, Audiophile 24/96; Digigram VX442;
- TerraTec EWX 24/96, EWS 88MT, 88D, DMX 6Fire, Phase 88;
- Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8.
+ TerraTec EWX 24/96, EWS 88MT/D, DMX 6Fire, Phase 88;
+ Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8;
+ Lionstracs Mediastation, Terrasoniq TS 88.
To compile this driver as a module, choose M here: the module
will be called snd-ice1712.
config SND_ICE1724
tristate "ICE/VT1724/1720 (Envy24HT/PT)"
- depends on SND
- select SND_MPU401_UART
+ select SND_RAWMIDI
select SND_AC97_CODEC
select SND_VMASTER
help
@@ -700,16 +682,18 @@ config SND_ICE1724
ICE/VT1724/1720 (Envy24HT/PT) chips.
Currently supported hardware is: AMP AUDIO2000; M-Audio
- Revolution 7.1; TerraTec Aureon 5.1 Sky, 7.1 Space/Universe;
- AudioTrak Prodigy 7.1; Pontis MS300; Albatron K8X800 Pro II;
- Chaintech ZNF3-150/250.
+ Revolution 5.1, 7.1, Audiophile 192; TerraTec Aureon 5.1 Sky,
+ 7.1 Space/Universe, Phase 22/28; Onkyo SE-90PCI, SE-200PCI;
+ AudioTrak Prodigy 192, 7.1 (HIFI/LT/XT), HD2; Hercules
+ Fortissimo IV; ESI Juli@; Pontis MS300; EGO-SYS WaveTerminal
+ 192M; Albatron K8X800 Pro II; Chaintech ZNF3-150/250, 9CJS,
+ AV-710; Shuttle SN25P.
To compile this driver as a module, choose M here: the module
will be called snd-ice1724.
config SND_INTEL8X0
tristate "Intel/SiS/nVidia/AMD/ALi AC97 Controller"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated AC97 sound
@@ -722,7 +706,6 @@ config SND_INTEL8X0
config SND_INTEL8X0M
tristate "Intel/SiS/nVidia/AMD MC97 Modem"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated MC97 modem on
@@ -733,8 +716,6 @@ config SND_INTEL8X0M
config SND_KORG1212
tristate "Korg 1212 IO"
- depends on SND
- select FW_LOADER if !SND_KORG1212_FIRMWARE_IN_KERNEL
select SND_PCM
help
Say Y here to include support for Korg 1212IO soundcards.
@@ -742,19 +723,8 @@ config SND_KORG1212
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
-config SND_KORG1212_FIRMWARE_IN_KERNEL
- bool "In-kernel firmware for Korg1212 driver"
- depends on SND_KORG1212
- default y
- help
- Say Y here to include the static firmware built in the kernel
- for the Korg1212 driver. If you choose N here, you need to
- install the firmware files from the alsa-firmware package.
-
config SND_MAESTRO3
tristate "ESS Allegro/Maestro3"
- depends on SND
- select FW_LOADER if !SND_MAESTRO3_FIRMWARE_IN_KERNEL
select SND_AC97_CODEC
help
Say Y here to include support for soundcards based on ESS Maestro 3
@@ -763,18 +733,8 @@ config SND_MAESTRO3
To compile this driver as a module, choose M here: the module
will be called snd-maestro3.
-config SND_MAESTRO3_FIRMWARE_IN_KERNEL
- bool "In-kernel firmware for Maestro3 driver"
- depends on SND_MAESTRO3
- default y
- help
- Say Y here to include the static firmware built in the kernel
- for the Maestro3 driver. If you choose N here, you need to
- install the firmware files from the alsa-firmware package.
-
config SND_MIXART
tristate "Digigram miXart"
- depends on SND
select SND_HWDEP
select SND_PCM
help
@@ -786,7 +746,6 @@ config SND_MIXART
config SND_NM256
tristate "NeoMagic NM256AV/ZX"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for NeoMagic NM256AV/ZX chips.
@@ -796,7 +755,6 @@ config SND_NM256
config SND_PCXHR
tristate "Digigram PCXHR"
- depends on SND
select SND_PCM
select SND_HWDEP
help
@@ -807,7 +765,6 @@ config SND_PCXHR
config SND_RIPTIDE
tristate "Conexant Riptide"
- depends on SND
select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -820,7 +777,6 @@ config SND_RIPTIDE
config SND_RME32
tristate "RME Digi32, 32/8, 32 PRO"
- depends on SND
select SND_PCM
help
Say Y to include support for RME Digi32, Digi32 PRO and
@@ -832,7 +788,6 @@ config SND_RME32
config SND_RME96
tristate "RME Digi96, 96/8, 96/8 PRO"
- depends on SND
select SND_PCM
help
Say Y here to include support for RME Digi96, Digi96/8 and
@@ -843,7 +798,6 @@ config SND_RME96
config SND_RME9652
tristate "RME Digi9652 (Hammerfall)"
- depends on SND
select SND_PCM
help
Say Y here to include support for RME Hammerfall (RME
@@ -854,7 +808,7 @@ config SND_RME9652
config SND_SIS7019
tristate "SiS 7019 Audio Accelerator"
- depends on SND && X86 && !X86_64
+ depends on X86 && !X86_64
select SND_AC97_CODEC
help
Say Y here to include support for the SiS 7019 Audio Accelerator.
@@ -864,7 +818,6 @@ config SND_SIS7019
config SND_SONICVIBES
tristate "S3 SonicVibes"
- depends on SND
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -877,7 +830,6 @@ config SND_SONICVIBES
config SND_TRIDENT
tristate "Trident 4D-Wave DX/NX; SiS 7018"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -889,7 +841,6 @@ config SND_TRIDENT
config SND_VIA82XX
tristate "VIA 82C686A/B, 8233/8235 AC97 Controller"
- depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -901,7 +852,6 @@ config SND_VIA82XX
config SND_VIA82XX_MODEM
tristate "VIA 82C686A/B, 8233 based Modems"
- depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated MC97 modem on
@@ -912,18 +862,17 @@ config SND_VIA82XX_MODEM
config SND_VIRTUOSO
tristate "Asus Virtuoso 100/200 (Xonar)"
- depends on SND
select SND_OXYGEN_LIB
help
Say Y here to include support for sound cards based on the
- Asus AV100/AV200 chips, i.e., Xonar D2, DX and D2X.
+ Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X and
+ HDAV1.3 (Deluxe).
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
select SND_VX_LIB
help
Say Y here to include support for Digigram VX222 soundcards.
@@ -933,8 +882,6 @@ config SND_VX222
config SND_YMFPCI
tristate "Yamaha YMF724/740/744/754"
- depends on SND
- select FW_LOADER if !SND_YMFPCI_FIRMWARE_IN_KERNEL
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -945,34 +892,4 @@ config SND_YMFPCI
To compile this driver as a module, choose M here: the module
will be called snd-ymfpci.
-config SND_YMFPCI_FIRMWARE_IN_KERNEL
- bool "In-kernel firmware for YMFPCI driver"
- depends on SND_YMFPCI
- default y
- help
- Say Y here to include the static firmware built in the kernel
- for the YMFPCI driver. If you choose N here, you need to
- install the firmware files from the alsa-firmware package.
-
-config SND_AC97_POWER_SAVE
- bool "AC97 Power-Saving Mode"
- depends on SND_AC97_CODEC && EXPERIMENTAL
- default n
- help
- Say Y here to enable the aggressive power-saving support of
- AC97 codecs. In this mode, the power-mode is dynamically
- controlled at each open/close.
-
- The mode is activated by passing power_save=1 option to
- snd-ac97-codec driver. You can toggle it dynamically over
- sysfs, too.
-
-config SND_AC97_POWER_SAVE_DEFAULT
- int "Default time-out for AC97 power-save mode"
- depends on SND_AC97_POWER_SAVE
- default 0
- help
- The default time-out value in seconds for AC97 automatic
- power-save mode. 0 means to disable the power-save mode.
-
-endmenu
+endif # SND_PCI
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 85ef14bc805..65b25d221cd 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -13,7 +13,7 @@ snd-bt87x-objs := bt87x.o
snd-cmipci-objs := cmipci.o
snd-cs4281-objs := cs4281.o
snd-cs5530-objs := cs5530.o
-snd-ens1370-objs := ens1370.o
+snd-ens1370-objs := ens1370.o ak4531_codec.o
snd-ens1371-objs := ens1371.o
snd-es1938-objs := es1938.o
snd-es1968-objs := es1968.o
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 0be48b1a22d..41fa322f097 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -3,16 +3,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ac97-codec-objs := ac97_codec.o ac97_pcm.o
-
-ifneq ($(CONFIG_PROC_FS),)
-snd-ac97-codec-objs += ac97_proc.o
-endif
-
-snd-ak4531-codec-objs := ak4531_codec.o
+snd-ac97-codec-y := ac97_codec.o ac97_pcm.o
+snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
-obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o
-
-obj-m := $(sort $(obj-m))
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 45fd29017dd..6704acbca8c 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -49,8 +49,9 @@ MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
#ifdef CONFIG_SND_AC97_POWER_SAVE
static int power_save = CONFIG_SND_AC97_POWER_SAVE_DEFAULT;
-module_param(power_save, bool, 0644);
-MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+ "(in second, 0 = disable).");
#endif
/*
@@ -66,8 +67,8 @@ struct ac97_codec_id {
};
static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
-{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL },
{ 0x41445300, 0xffffff00, "Analog Devices", NULL, NULL },
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL },
{ 0x414c4300, 0xffffff00, "Realtek", NULL, NULL },
{ 0x414c4700, 0xffffff00, "Realtek", NULL, NULL },
{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL },
@@ -93,11 +94,6 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
};
static const struct ac97_codec_id snd_ac97_codec_ids[] = {
-{ 0x414b4d00, 0xffffffff, "AK4540", NULL, NULL },
-{ 0x414b4d01, 0xffffffff, "AK4542", NULL, NULL },
-{ 0x414b4d02, 0xffffffff, "AK4543", NULL, NULL },
-{ 0x414b4d06, 0xffffffff, "AK4544A", NULL, NULL },
-{ 0x414b4d07, 0xffffffff, "AK4545", NULL, NULL },
{ 0x41445303, 0xffffffff, "AD1819", patch_ad1819, NULL },
{ 0x41445340, 0xffffffff, "AD1881", patch_ad1881, NULL },
{ 0x41445348, 0xffffffff, "AD1881A", patch_ad1881, NULL },
@@ -111,20 +107,25 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL },
{ 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL },
{ 0x41445378, 0xffffffff, "AD1986", patch_ad1986, NULL },
+{ 0x414b4d00, 0xffffffff, "AK4540", NULL, NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542", NULL, NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543", NULL, NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A", NULL, NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545", NULL, NULL },
{ 0x414c4300, 0xffffff00, "ALC100,100P", NULL, NULL },
{ 0x414c4710, 0xfffffff0, "ALC200,200P", NULL, NULL },
{ 0x414c4721, 0xffffffff, "ALC650D", NULL, NULL }, /* already patched */
{ 0x414c4722, 0xffffffff, "ALC650E", NULL, NULL }, /* already patched */
{ 0x414c4723, 0xffffffff, "ALC650F", NULL, NULL }, /* already patched */
{ 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL },
+{ 0x414c4730, 0xffffffff, "ALC101", NULL, NULL },
+{ 0x414c4740, 0xfffffff0, "ALC202", NULL, NULL },
+{ 0x414c4750, 0xfffffff0, "ALC250", NULL, NULL },
{ 0x414c4760, 0xfffffff0, "ALC655", patch_alc655, NULL },
+{ 0x414c4770, 0xfffffff0, "ALC203", patch_alc203, NULL },
{ 0x414c4781, 0xffffffff, "ALC658D", NULL, NULL }, /* already patched */
{ 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL },
{ 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL },
-{ 0x414c4730, 0xffffffff, "ALC101", NULL, NULL },
-{ 0x414c4740, 0xfffffff0, "ALC202", NULL, NULL },
-{ 0x414c4750, 0xfffffff0, "ALC250", NULL, NULL },
-{ 0x414c4770, 0xfffffff0, "ALC203", NULL, NULL },
{ 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL },
{ 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL },
{ 0x434d4969, 0xffffffff, "CMI9780", patch_cm9780, NULL },
@@ -160,15 +161,16 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL },
{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH },
{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
+{ 0x54524103, 0xffffffff, "TR28023", NULL, NULL },
{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL },
{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028, NULL }, // added by xin jin [07/09/99]
{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL },
{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF
{ 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF
-{ 0x56494182, 0xffffffff, "VIA1618", NULL, NULL },
+{ 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL },
{ 0x57454301, 0xffffffff, "W83971D", NULL, NULL },
-{ 0x574d4c00, 0xffffffff, "WM9701A", NULL, NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701,WM9701A", NULL, NULL },
{ 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q", patch_wolfson04, NULL},
{ 0x574d4C05, 0xffffffff, "WM9705,WM9710", patch_wolfson05, NULL},
@@ -1888,8 +1890,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
.dev_free = snd_ac97_bus_dev_free,
};
- snd_assert(card != NULL, return -EINVAL);
- snd_assert(rbus != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
bus = kzalloc(sizeof(*bus), GFP_KERNEL);
if (bus == NULL)
return -ENOMEM;
@@ -1904,7 +1906,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
snd_ac97_bus_free(bus);
return err;
}
- *rbus = bus;
+ if (rbus)
+ *rbus = bus;
return 0;
}
@@ -1989,10 +1992,14 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
.dev_disconnect = snd_ac97_dev_disconnect,
};
- snd_assert(rac97 != NULL, return -EINVAL);
- *rac97 = NULL;
- snd_assert(bus != NULL && template != NULL, return -EINVAL);
- snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL);
+ if (rac97)
+ *rac97 = NULL;
+ if (snd_BUG_ON(!bus || !template))
+ return -EINVAL;
+ if (snd_BUG_ON(template->num >= 4))
+ return -EINVAL;
+ if (bus->codec[template->num])
+ return -EBUSY;
card = bus->card;
ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL);
@@ -2294,9 +2301,11 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
udelay(100);
- power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */
+ power |= AC97_PD_PR2; /* Analog Mixer powerdown (Vref on) */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
if (ac97_is_power_save_mode(ac97)) {
+ power |= AC97_PD_PR3; /* Analog Mixer powerdown */
+ snd_ac97_write(ac97, AC97_POWERDOWN, power);
udelay(100);
/* AC-link powerdown, internal Clk disable */
/* FIXME: this may cause click noises on some boards */
@@ -2362,7 +2371,7 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
* that open/close frequently)
*/
schedule_delayed_work(&ac97->power_work,
- msecs_to_jiffies(2000));
+ msecs_to_jiffies(power_save * 1000));
else {
cancel_delayed_work(&ac97->power_work);
update_power_regs(ac97);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 1292dcee072..6e831aff1bd 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -476,7 +476,7 @@ static int patch_yamaha_ymf753(struct snd_ac97 * ac97)
}
/*
- * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * May 2, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
* removed broken wolfson00 patch.
* added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
*/
@@ -669,6 +669,7 @@ AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
AC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
+AC97_SINGLE("Master Left Inv Switch", AC97_MASTER, 6, 1, 0),
AC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0),
AC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0),
AC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
@@ -2559,6 +2560,14 @@ static int patch_ad1986(struct snd_ac97 * ac97)
return 0;
}
+/*
+ * realtek ALC203: use mono-out for pin 37
+ */
+static int patch_alc203(struct snd_ac97 *ac97)
+{
+ snd_ac97_update_bits(ac97, 0x7a, 0x400, 0x400);
+ return 0;
+}
/*
* realtek ALC65x/850 codecs
@@ -3352,8 +3361,66 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
};
+static const char *slave_vols_vt1616[] = {
+ "Front Playback Volume",
+ "Surround Playback Volume",
+ "Center Playback Volume",
+ "LFE Playback Volume",
+ NULL
+};
+
+static const char *slave_sws_vt1616[] = {
+ "Front Playback Switch",
+ "Surround Playback Switch",
+ "Center Playback Switch",
+ "LFE Playback Switch",
+ NULL
+};
+
+/* find a mixer control element with the given name */
+static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
+ 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(ac97->bus->card, &id);
+}
+
+/* create a virtual master control and add slaves */
+static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+ const 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(ac97->bus->card, kctl);
+ if (err < 0)
+ return err;
+
+ for (s = slaves; *s; s++) {
+ struct snd_kcontrol *sctl;
+
+ sctl = snd_ac97_find_mixer_ctl(ac97, *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;
+}
+
static int patch_vt1616_specific(struct snd_ac97 * ac97)
{
+ struct snd_kcontrol *kctl;
int err;
if (snd_ac97_try_bit(ac97, 0x5a, 9))
@@ -3361,6 +3428,24 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97)
return err;
if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
return err;
+
+ /* There is already a misnamed master switch. Rename it. */
+ kctl = snd_ac97_find_mixer_ctl(ac97, "Master Playback Volume");
+ if (!kctl)
+ return -EINVAL;
+
+ snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Front Playback");
+
+ err = snd_ac97_add_vmaster(ac97, "Master Playback Volume",
+ kctl->tlv.p, slave_vols_vt1616);
+ if (err < 0)
+ return err;
+
+ err = snd_ac97_add_vmaster(ac97, "Master Playback Switch",
+ NULL, slave_sws_vt1616);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -3380,7 +3465,7 @@ static int patch_vt1616(struct snd_ac97 * ac97)
/*
* unfortunately, the vt1617a stashes the twiddlers required for
- * nooding the i/o jacks on 2 different regs. * thameans that we cant
+ * noodling the i/o jacks on 2 different regs. that means that we can't
* use the easy way provided by AC97_ENUM_DOUBLE() we have to write
* are own funcs.
*
@@ -3413,7 +3498,7 @@ static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol,
pac97 = snd_kcontrol_chip(kcontrol); /* grab codec handle */
- /* grab our desirec bits, then mash them together in a manner
+ /* grab our desired bits, then mash them together in a manner
* consistent with Table 6 on page 17 in the 1617a docs */
usSM51 = snd_ac97_read(pac97, 0x7a) >> 14;
@@ -3463,7 +3548,7 @@ static const struct snd_kcontrol_new snd_ac97_controls_vt1617a[] = {
},
};
-int patch_vt1617a(struct snd_ac97 * ac97)
+static int patch_vt1617a(struct snd_ac97 * ac97)
{
int err = 0;
int val;
@@ -3491,6 +3576,200 @@ int patch_vt1617a(struct snd_ac97 * ac97)
return err;
}
+/* VIA VT1618 8 CHANNEL AC97 CODEC
+ *
+ * VIA implements 'Smart 5.1' completely differently on the 1618 than
+ * it does on the 1617a. awesome! They seem to have sourced this
+ * particular revision of the technology from somebody else, it's
+ * called Universal Audio Jack and it shows up on some other folk's chips
+ * as well.
+ *
+ * ordering in this list reflects vt1618 docs for Reg 60h and
+ * the block diagram, DACs are as follows:
+ *
+ * OUT_O -> Front,
+ * OUT_1 -> Surround,
+ * OUT_2 -> C/LFE
+ *
+ * Unlike the 1617a, each OUT has a consistent set of mappings
+ * for all bitpatterns other than 00:
+ *
+ * 01 Unmixed Output
+ * 10 Line In
+ * 11 Mic In
+ *
+ * Special Case of 00:
+ *
+ * OUT_0 Mixed Output
+ * OUT_1 Reserved
+ * OUT_2 Reserved
+ *
+ * I have no idea what the hell Reserved does, but on an MSI
+ * CN700T, i have to set it to get 5.1 output - YMMV, bad
+ * shit may happen.
+ *
+ * If other chips use Universal Audio Jack, then this code might be applicable
+ * to them.
+ */
+
+struct vt1618_uaj_item {
+ unsigned short mask;
+ unsigned short shift;
+ const char *items[4];
+};
+
+/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */
+
+static struct vt1618_uaj_item vt1618_uaj[3] = {
+ {
+ /* speaker jack */
+ .mask = 0x03,
+ .shift = 0,
+ .items = {
+ "Speaker Out", "DAC Unmixed Out", "Line In", "Mic In"
+ }
+ },
+ {
+ /* line jack */
+ .mask = 0x0c,
+ .shift = 2,
+ .items = {
+ "Surround Out", "DAC Unmixed Out", "Line In", "Mic In"
+ }
+ },
+ {
+ /* mic jack */
+ .mask = 0x30,
+ .shift = 4,
+ .items = {
+ "Center LFE Out", "DAC Unmixed Out", "Line In", "Mic In"
+ },
+ },
+};
+
+static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return ac97_enum_text_info(kcontrol, uinfo,
+ vt1618_uaj[kcontrol->private_value].items,
+ 4);
+}
+
+/* All of the vt1618 Universal Audio Jack twiddlers are on
+ * Vendor Defined Register 0x60, page 0. The bits, and thus
+ * the mask, are the only thing that changes
+ */
+static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned short datpag, uaj;
+ struct snd_ac97 *pac97 = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&pac97->page_mutex);
+
+ datpag = snd_ac97_read(pac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
+ snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, 0);
+
+ uaj = snd_ac97_read(pac97, 0x60) &
+ vt1618_uaj[kcontrol->private_value].mask;
+
+ snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, datpag);
+ mutex_unlock(&pac97->page_mutex);
+
+ ucontrol->value.enumerated.item[0] = uaj >>
+ vt1618_uaj[kcontrol->private_value].shift;
+
+ return 0;
+}
+
+static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return ac97_update_bits_page(snd_kcontrol_chip(kcontrol), 0x60,
+ vt1618_uaj[kcontrol->private_value].mask,
+ ucontrol->value.enumerated.item[0]<<
+ vt1618_uaj[kcontrol->private_value].shift,
+ 0);
+}
+
+/* config aux in jack - not found on 3 jack motherboards or soundcards */
+
+static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *txt_aux[] = {"Aux In", "Back Surr Out"};
+
+ return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2);
+}
+
+static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] =
+ (snd_ac97_read(snd_kcontrol_chip(kcontrol), 0x5c) & 0x0008)>>3;
+ return 0;
+}
+
+static int snd_ac97_vt1618_aux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* toggle surround rear dac power */
+
+ snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x5c, 0x0008,
+ ucontrol->value.enumerated.item[0] << 3);
+
+ /* toggle aux in surround rear out jack */
+
+ return snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x76, 0x0008,
+ ucontrol->value.enumerated.item[0] << 3);
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_vt1618[] = {
+ AC97_SINGLE("Exchange Center/LFE", 0x5a, 8, 1, 0),
+ AC97_SINGLE("DC Offset", 0x5a, 10, 1, 0),
+ AC97_SINGLE("Soft Mute", 0x5c, 0, 1, 1),
+ AC97_SINGLE("Headphone Amp", 0x5c, 5, 1, 1),
+ AC97_DOUBLE("Back Surr Volume", 0x5e, 8, 0, 31, 1),
+ AC97_SINGLE("Back Surr Switch", 0x5e, 15, 1, 1),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Speaker Jack Mode",
+ .info = snd_ac97_vt1618_UAJ_info,
+ .get = snd_ac97_vt1618_UAJ_get,
+ .put = snd_ac97_vt1618_UAJ_put,
+ .private_value = 0
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Jack Mode",
+ .info = snd_ac97_vt1618_UAJ_info,
+ .get = snd_ac97_vt1618_UAJ_get,
+ .put = snd_ac97_vt1618_UAJ_put,
+ .private_value = 1
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Jack Mode",
+ .info = snd_ac97_vt1618_UAJ_info,
+ .get = snd_ac97_vt1618_UAJ_get,
+ .put = snd_ac97_vt1618_UAJ_put,
+ .private_value = 2
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Jack Mode",
+ .info = snd_ac97_vt1618_aux_info,
+ .get = snd_ac97_vt1618_aux_get,
+ .put = snd_ac97_vt1618_aux_put,
+ }
+};
+
+static int patch_vt1618(struct snd_ac97 *ac97)
+{
+ return patch_build_controls(ac97, snd_ac97_controls_vt1618,
+ ARRAY_SIZE(snd_ac97_controls_vt1618));
+}
+
/*
*/
static void it2646_update_jacks(struct snd_ac97 *ac97)
@@ -3633,7 +3912,7 @@ static int patch_ucb1400(struct snd_ac97 * ac97)
{
ac97->build_ops = &patch_ucb1400_ops;
/* enable headphone driver and smart low power mode by default */
- snd_ac97_write(ac97, 0x6a, 0x0050);
- snd_ac97_write(ac97, 0x6c, 0x0030);
+ snd_ac97_write_cache(ac97, 0x6a, 0x0050);
+ snd_ac97_write_cache(ac97, 0x6c, 0x0030);
return 0;
}
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 39ec55b57b1..92f3a976ef2 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -549,7 +549,8 @@ snd_ad1889_playback_pointer(struct snd_pcm_substream *ss)
ptr = ad1889_readl(chip, AD_DMA_WAVCA);
ptr -= chip->wave.addr;
- snd_assert((ptr >= 0) && (ptr < chip->wave.size), return 0);
+ if (snd_BUG_ON(ptr >= chip->wave.size))
+ return 0;
return bytes_to_frames(ss->runtime, ptr);
}
@@ -567,7 +568,8 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss)
ptr = ad1889_readl(chip, AD_DMA_ADCCA);
ptr -= chip->ramc.addr;
- snd_assert((ptr >= 0) && (ptr < chip->ramc.size), return 0);
+ if (snd_BUG_ON(ptr >= chip->ramc.size))
+ return 0;
return bytes_to_frames(ss->runtime, ptr);
}
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ak4531_codec.c
index c0c1633999e..0f819ddb3eb 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -28,9 +28,11 @@
#include <sound/ak4531_codec.h>
#include <sound/tlv.h>
+/*
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Universal routines for AK4531 codec");
MODULE_LICENSE("GPL");
+*/
#ifdef CONFIG_PROC_FS
static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
@@ -270,7 +272,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
-static struct snd_kcontrol_new snd_ak4531_controls[] = {
+static struct snd_kcontrol_new snd_ak4531_controls[] __devinitdata = {
AK4531_DOUBLE_TLV("Master Playback Switch", 0,
AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
@@ -379,8 +381,9 @@ static u8 snd_ak4531_initial_map[0x19 + 1] = {
0x01 /* 19: Mic Amp Setup */
};
-int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
- struct snd_ak4531 **rak4531)
+int __devinit snd_ak4531_mixer(struct snd_card *card,
+ struct snd_ak4531 *_ak4531,
+ struct snd_ak4531 **rak4531)
{
unsigned int idx;
int err;
@@ -389,9 +392,10 @@ int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
.dev_free = snd_ak4531_dev_free,
};
- snd_assert(rak4531 != NULL, return -EINVAL);
- *rak4531 = NULL;
- snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card || !_ak4531))
+ return -EINVAL;
+ if (rak4531)
+ *rak4531 = NULL;
ak4531 = kzalloc(sizeof(*ak4531), GFP_KERNEL);
if (ak4531 == NULL)
return -ENOMEM;
@@ -425,7 +429,8 @@ int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
#if 0
snd_ak4531_dump(ak4531);
#endif
- *rak4531 = ak4531;
+ if (rak4531)
+ *rak4531 = ak4531;
return 0;
}
@@ -476,7 +481,8 @@ static void snd_ak4531_proc_read(struct snd_info_entry *entry,
ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
}
-static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
+static void __devinit
+snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
{
struct snd_info_entry *entry;
@@ -484,25 +490,3 @@ static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak453
snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
}
#endif
-
-EXPORT_SYMBOL(snd_ak4531_mixer);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_ak4531_suspend);
-EXPORT_SYMBOL(snd_ak4531_resume);
-#endif
-
-/*
- * INIT part
- */
-
-static int __init alsa_ak4531_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ak4531_exit(void)
-{
-}
-
-module_init(alsa_ak4531_init)
-module_exit(alsa_ak4531_exit)
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 27ce6136ab0..ba570053d4d 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -2,7 +2,7 @@
* card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
* Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
* Jaroslav Kysela <perex@perex.cz>
- * Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
+ * Copyright (C) 2002, 2008 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
*
* Framework borrowed from Massimo Piccioni's card-als100.c.
*
@@ -27,8 +27,10 @@
* bought an ALS4000 based soundcard, I was forced to base this driver
* on reverse engineering.
*
- * Note: this is no longer true. Pretty verbose chip docu (ALS4000a.PDF)
- * can be found on the ALSA web site.
+ * Note: this is no longer true (thank you!):
+ * pretty verbose chip docu (ALS4000a.PDF) can be found on the ALSA web site.
+ * Page numbers stated anywhere below with the "SPECS_PAGE:" tag
+ * refer to: ALS4000a.PDF specs Ver 1.0, May 28th, 1998.
*
* The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an
* ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport
@@ -59,7 +61,7 @@
* - value -> some port 0x0c0d
*
* ToDo:
- * - Proper shared IRQ handling?
+ * - by default, don't enable legacy game and use PCI game I/O
* - power management? (card can do voice wakeup according to datasheet!!)
*/
@@ -78,7 +80,7 @@
#include <sound/sb.h>
#include <sound/initval.h>
-MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>");
+MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>, Andreas Mohr");
MODULE_DESCRIPTION("Avance Logic ALS4000");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}");
@@ -107,7 +109,7 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0
struct snd_card_als4000 {
/* most frequent access first */
- unsigned long gcr;
+ unsigned long iobase;
struct pci_dev *pci;
struct snd_sb *chip;
#ifdef SUPPORT_JOYSTICK
@@ -122,28 +124,168 @@ static struct pci_device_id snd_als4000_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_als4000_ids);
-static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val)
+enum als4k_iobase_t {
+ /* IOx: B == Byte, W = Word, D = DWord; SPECS_PAGE: 37 */
+ ALS4K_IOD_00_AC97_ACCESS = 0x00,
+ ALS4K_IOW_04_AC97_READ = 0x04,
+ ALS4K_IOB_06_AC97_STATUS = 0x06,
+ ALS4K_IOB_07_IRQSTATUS = 0x07,
+ ALS4K_IOD_08_GCR_DATA = 0x08,
+ ALS4K_IOB_0C_GCR_INDEX = 0x0c,
+ ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU = 0x0e,
+ ALS4K_IOB_10_ADLIB_ADDR0 = 0x10,
+ ALS4K_IOB_11_ADLIB_ADDR1 = 0x11,
+ ALS4K_IOB_12_ADLIB_ADDR2 = 0x12,
+ ALS4K_IOB_13_ADLIB_ADDR3 = 0x13,
+ ALS4K_IOB_14_MIXER_INDEX = 0x14,
+ ALS4K_IOB_15_MIXER_DATA = 0x15,
+ ALS4K_IOB_16_ESP_RESET = 0x16,
+ ALS4K_IOB_16_ACK_FOR_CR1E = 0x16, /* 2nd function */
+ ALS4K_IOB_18_OPL_ADDR0 = 0x18,
+ ALS4K_IOB_19_OPL_ADDR1 = 0x19,
+ ALS4K_IOB_1A_ESP_RD_DATA = 0x1a,
+ ALS4K_IOB_1C_ESP_CMD_DATA = 0x1c,
+ ALS4K_IOB_1C_ESP_WR_STATUS = 0x1c, /* 2nd function */
+ ALS4K_IOB_1E_ESP_RD_STATUS8 = 0x1e,
+ ALS4K_IOB_1F_ESP_RD_STATUS16 = 0x1f,
+ ALS4K_IOB_20_ESP_GAMEPORT_200 = 0x20,
+ ALS4K_IOB_21_ESP_GAMEPORT_201 = 0x21,
+ ALS4K_IOB_30_MIDI_DATA = 0x30,
+ ALS4K_IOB_31_MIDI_STATUS = 0x31,
+ ALS4K_IOB_31_MIDI_COMMAND = 0x31, /* 2nd function */
+};
+
+enum als4k_iobase_0e_t {
+ ALS4K_IOB_0E_MPU_IRQ = 0x10,
+ ALS4K_IOB_0E_CR1E_IRQ = 0x40,
+ ALS4K_IOB_0E_SB_DMA_IRQ = 0x80,
+};
+
+enum als4k_gcr_t { /* all registers 32bit wide; SPECS_PAGE: 38 to 42 */
+ ALS4K_GCR8C_MISC_CTRL = 0x8c,
+ ALS4K_GCR90_TEST_MODE_REG = 0x90,
+ ALS4K_GCR91_DMA0_ADDR = 0x91,
+ ALS4K_GCR92_DMA0_MODE_COUNT = 0x92,
+ ALS4K_GCR93_DMA1_ADDR = 0x93,
+ ALS4K_GCR94_DMA1_MODE_COUNT = 0x94,
+ ALS4K_GCR95_DMA3_ADDR = 0x95,
+ ALS4K_GCR96_DMA3_MODE_COUNT = 0x96,
+ ALS4K_GCR99_DMA_EMULATION_CTRL = 0x99,
+ ALS4K_GCRA0_FIFO1_CURRENT_ADDR = 0xa0,
+ ALS4K_GCRA1_FIFO1_STATUS_BYTECOUNT = 0xa1,
+ ALS4K_GCRA2_FIFO2_PCIADDR = 0xa2,
+ ALS4K_GCRA3_FIFO2_COUNT = 0xa3,
+ ALS4K_GCRA4_FIFO2_CURRENT_ADDR = 0xa4,
+ ALS4K_GCRA5_FIFO1_STATUS_BYTECOUNT = 0xa5,
+ ALS4K_GCRA6_PM_CTRL = 0xa6,
+ ALS4K_GCRA7_PCI_ACCESS_STORAGE = 0xa7,
+ ALS4K_GCRA8_LEGACY_CFG1 = 0xa8,
+ ALS4K_GCRA9_LEGACY_CFG2 = 0xa9,
+ ALS4K_GCRFF_DUMMY_SCRATCH = 0xff,
+};
+
+enum als4k_gcr8c_t {
+ ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE = 0x8000,
+ ALS4K_GCR8C_CHIP_REV_MASK = 0xf0000
+};
+
+static inline void snd_als4k_iobase_writeb(unsigned long iobase,
+ enum als4k_iobase_t reg,
+ u8 val)
+{
+ outb(val, iobase + reg);
+}
+
+static inline void snd_als4k_iobase_writel(unsigned long iobase,
+ enum als4k_iobase_t reg,
+ u32 val)
+{
+ outl(val, iobase + reg);
+}
+
+static inline u8 snd_als4k_iobase_readb(unsigned long iobase,
+ enum als4k_iobase_t reg)
+{
+ return inb(iobase + reg);
+}
+
+static inline u32 snd_als4k_iobase_readl(unsigned long iobase,
+ enum als4k_iobase_t reg)
+{
+ return inl(iobase + reg);
+}
+
+static inline void snd_als4k_gcr_write_addr(unsigned long iobase,
+ enum als4k_gcr_t reg,
+ u32 val)
{
- outb(reg, port+0x0c);
- outl(val, port+0x08);
+ snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg);
+ snd_als4k_iobase_writel(iobase, ALS4K_IOD_08_GCR_DATA, val);
}
-static inline void snd_als4000_gcr_write(struct snd_sb *sb, u32 reg, u32 val)
+static inline void snd_als4k_gcr_write(struct snd_sb *sb,
+ enum als4k_gcr_t reg,
+ u32 val)
{
- snd_als4000_gcr_write_addr(sb->alt_port, reg, val);
+ snd_als4k_gcr_write_addr(sb->alt_port, reg, val);
}
-static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg)
+static inline u32 snd_als4k_gcr_read_addr(unsigned long iobase,
+ enum als4k_gcr_t reg)
{
- outb(reg, port+0x0c);
- return inl(port+0x08);
+ /* SPECS_PAGE: 37/38 */
+ snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg);
+ return snd_als4k_iobase_readl(iobase, ALS4K_IOD_08_GCR_DATA);
}
-static inline u32 snd_als4000_gcr_read(struct snd_sb *sb, u32 reg)
+static inline u32 snd_als4k_gcr_read(struct snd_sb *sb, enum als4k_gcr_t reg)
{
- return snd_als4000_gcr_read_addr(sb->alt_port, reg);
+ return snd_als4k_gcr_read_addr(sb->alt_port, reg);
}
+enum als4k_cr_t { /* all registers 8bit wide; SPECS_PAGE: 20 to 23 */
+ ALS4K_CR0_SB_CONFIG = 0x00,
+ ALS4K_CR2_MISC_CONTROL = 0x02,
+ ALS4K_CR3_CONFIGURATION = 0x03,
+ ALS4K_CR17_FIFO_STATUS = 0x17,
+ ALS4K_CR18_ESP_MAJOR_VERSION = 0x18,
+ ALS4K_CR19_ESP_MINOR_VERSION = 0x19,
+ ALS4K_CR1A_MPU401_UART_MODE_CONTROL = 0x1a,
+ ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO = 0x1c,
+ ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI = 0x1d,
+ ALS4K_CR1E_FIFO2_CONTROL = 0x1e, /* secondary PCM FIFO (recording) */
+ ALS4K_CR3A_MISC_CONTROL = 0x3a,
+ ALS4K_CR3B_CRC32_BYTE0 = 0x3b, /* for testing, activate via CR3A */
+ ALS4K_CR3C_CRC32_BYTE1 = 0x3c,
+ ALS4K_CR3D_CRC32_BYTE2 = 0x3d,
+ ALS4K_CR3E_CRC32_BYTE3 = 0x3e,
+};
+
+enum als4k_cr0_t {
+ ALS4K_CR0_DMA_CONTIN_MODE_CTRL = 0x02, /* IRQ/FIFO controlled for 0/1 */
+ ALS4K_CR0_DMA_90H_MODE_CTRL = 0x04, /* IRQ/FIFO controlled for 0/1 */
+ ALS4K_CR0_MX80_81_REG_WRITE_ENABLE = 0x80,
+};
+
+static inline void snd_als4_cr_write(struct snd_sb *chip,
+ enum als4k_cr_t reg,
+ u8 data)
+{
+ /* Control Register is reg | 0xc0 (bit 7, 6 set) on sbmixer_index
+ * NOTE: assumes chip->mixer_lock to be locked externally already!
+ * SPECS_PAGE: 6 */
+ snd_sbmixer_write(chip, reg | 0xc0, data);
+}
+
+static inline u8 snd_als4_cr_read(struct snd_sb *chip,
+ enum als4k_cr_t reg)
+{
+ /* NOTE: assumes chip->mixer_lock to be locked externally already! */
+ return snd_sbmixer_read(chip, reg | 0xc0);
+}
+
+
+
static void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)
{
if (!(chip->mode & SB_RATE_LOCK)) {
@@ -156,15 +298,19 @@ static void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)
static inline void snd_als4000_set_capture_dma(struct snd_sb *chip,
dma_addr_t addr, unsigned size)
{
- snd_als4000_gcr_write(chip, 0xa2, addr);
- snd_als4000_gcr_write(chip, 0xa3, (size-1));
+ /* SPECS_PAGE: 40 */
+ snd_als4k_gcr_write(chip, ALS4K_GCRA2_FIFO2_PCIADDR, addr);
+ snd_als4k_gcr_write(chip, ALS4K_GCRA3_FIFO2_COUNT, (size-1));
}
static inline void snd_als4000_set_playback_dma(struct snd_sb *chip,
- dma_addr_t addr, unsigned size)
+ dma_addr_t addr,
+ unsigned size)
{
- snd_als4000_gcr_write(chip, 0x91, addr);
- snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000);
+ /* SPECS_PAGE: 38 */
+ snd_als4k_gcr_write(chip, ALS4K_GCR91_DMA0_ADDR, addr);
+ snd_als4k_gcr_write(chip, ALS4K_GCR92_DMA0_MODE_COUNT,
+ (size-1)|0x180000);
}
#define ALS4000_FORMAT_SIGNED (1<<0)
@@ -248,7 +394,7 @@ static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
count = snd_pcm_lib_period_bytes(substream);
if (chip->capture_format & ALS4000_FORMAT_16BIT)
- count >>=1;
+ count >>= 1;
count--;
spin_lock_irq(&chip->reg_lock);
@@ -256,8 +402,8 @@ static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
spin_unlock_irq(&chip->reg_lock);
spin_lock_irq(&chip->mixer_lock);
- snd_sbmixer_write(chip, 0xdc, count);
- snd_sbmixer_write(chip, 0xdd, count>>8);
+ snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
+ snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
spin_unlock_irq(&chip->mixer_lock);
return 0;
}
@@ -275,7 +421,7 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
count = snd_pcm_lib_period_bytes(substream);
if (chip->playback_format & ALS4000_FORMAT_16BIT)
- count >>=1;
+ count >>= 1;
count--;
/* FIXME: from second playback on, there's a lot more clicks and pops
@@ -292,8 +438,8 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
/* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */
snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);
snd_sbdsp_command(chip, playback_cmd(chip).format);
- snd_sbdsp_command(chip, count);
- snd_sbdsp_command(chip, count>>8);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
spin_unlock_irq(&chip->reg_lock);
@@ -305,17 +451,25 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int
struct snd_sb *chip = snd_pcm_substream_chip(substream);
int result = 0;
+ /* FIXME race condition in here!!!
+ chip->mode non-atomic update gets consistently protected
+ by reg_lock always, _except_ for this place!!
+ Probably need to take reg_lock as outer (or inner??) lock, too.
+ (or serialize both lock operations? probably not, though... - racy?)
+ */
spin_lock(&chip->mixer_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
chip->mode |= SB_RATE_LOCK_CAPTURE;
- snd_sbmixer_write(chip, 0xde, capture_cmd(chip));
+ snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL,
+ capture_cmd(chip));
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
chip->mode &= ~SB_RATE_LOCK_CAPTURE;
- snd_sbmixer_write(chip, 0xde, 0);
+ snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL,
+ capture_cmd(chip));
break;
default:
result = -EINVAL;
@@ -356,8 +510,9 @@ static snd_pcm_uframes_t snd_als4000_capture_pointer(struct snd_pcm_substream *s
unsigned int result;
spin_lock(&chip->reg_lock);
- result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff;
+ result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
spin_unlock(&chip->reg_lock);
+ result &= 0xffff;
return bytes_to_frames( substream->runtime, result );
}
@@ -367,8 +522,9 @@ static snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *
unsigned result;
spin_lock(&chip->reg_lock);
- result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff;
+ result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
spin_unlock(&chip->reg_lock);
+ result &= 0xffff;
return bytes_to_frames( substream->runtime, result );
}
@@ -376,45 +532,63 @@ static snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *
* return IRQ_HANDLED no matter whether we actually had an IRQ flag or not).
* ALS4000a.PDF writes that while ACKing IRQ in PCI block will *not* ACK
* the IRQ in the SB core, ACKing IRQ in SB block *will* ACK the PCI IRQ
- * register (alt_port + 0x0e). Probably something could be optimized here to
- * query/write one register only...
+ * register (alt_port + ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU). Probably something
+ * could be optimized here to query/write one register only...
* And even if both registers need to be queried, then there's still the
* question of whether it's actually correct to ACK PCI IRQ before reading
* SB IRQ like we do now, since ALS4000a.PDF mentions that PCI IRQ will *clear*
* SB IRQ status.
+ * (hmm, SPECS_PAGE: 38 mentions it the other way around!)
* And do we *really* need the lock here for *reading* SB_DSP4_IRQSTATUS??
* */
static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
{
struct snd_sb *chip = dev_id;
- unsigned gcr_status;
- unsigned sb_status;
-
- /* find out which bit of the ALS4000 produced the interrupt */
- gcr_status = inb(chip->alt_port + 0xe);
-
- if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */
+ unsigned pci_irqstatus;
+ unsigned sb_irqstatus;
+
+ /* find out which bit of the ALS4000 PCI block produced the interrupt,
+ SPECS_PAGE: 38, 5 */
+ pci_irqstatus = snd_als4k_iobase_readb(chip->alt_port,
+ ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU);
+ if ((pci_irqstatus & ALS4K_IOB_0E_SB_DMA_IRQ)
+ && (chip->playback_substream)) /* playback */
snd_pcm_period_elapsed(chip->playback_substream);
- if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
+ if ((pci_irqstatus & ALS4K_IOB_0E_CR1E_IRQ)
+ && (chip->capture_substream)) /* capturing */
snd_pcm_period_elapsed(chip->capture_substream);
- if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */
+ if ((pci_irqstatus & ALS4K_IOB_0E_MPU_IRQ)
+ && (chip->rmidi)) /* MPU401 interrupt */
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
- /* release the gcr */
- outb(gcr_status, chip->alt_port + 0xe);
+ /* ACK the PCI block IRQ */
+ snd_als4k_iobase_writeb(chip->alt_port,
+ ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU, pci_irqstatus);
spin_lock(&chip->mixer_lock);
- sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ /* SPECS_PAGE: 20 */
+ sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
spin_unlock(&chip->mixer_lock);
- if (sb_status & SB_IRQTYPE_8BIT)
+ if (sb_irqstatus & SB_IRQTYPE_8BIT)
snd_sb_ack_8bit(chip);
- if (sb_status & SB_IRQTYPE_16BIT)
+ if (sb_irqstatus & SB_IRQTYPE_16BIT)
snd_sb_ack_16bit(chip);
- if (sb_status & SB_IRQTYPE_MPUIN)
+ if (sb_irqstatus & SB_IRQTYPE_MPUIN)
inb(chip->mpu_port);
- if (sb_status & 0x20)
- inb(SBP(chip, RESET));
- return IRQ_HANDLED;
+ if (sb_irqstatus & ALS4K_IRQTYPE_CR1E_DMA)
+ snd_als4k_iobase_readb(chip->alt_port,
+ ALS4K_IOB_16_ACK_FOR_CR1E);
+
+ /* printk(KERN_INFO "als4000: irq 0x%04x 0x%04x\n",
+ pci_irqstatus, sb_irqstatus); */
+
+ /* only ack the things we actually handled above */
+ return IRQ_RETVAL(
+ (pci_irqstatus & (ALS4K_IOB_0E_SB_DMA_IRQ|ALS4K_IOB_0E_CR1E_IRQ|
+ ALS4K_IOB_0E_MPU_IRQ))
+ || (sb_irqstatus & (SB_IRQTYPE_8BIT|SB_IRQTYPE_16BIT|
+ SB_IRQTYPE_MPUIN|ALS4K_IRQTYPE_CR1E_DMA))
+ );
}
/*****************************************************************/
@@ -526,7 +700,8 @@ static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
@@ -543,48 +718,55 @@ static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
/******************************************************************/
-static void snd_als4000_set_addr(unsigned long gcr,
- unsigned int sb,
- unsigned int mpu,
- unsigned int opl,
- unsigned int game)
+static void snd_als4000_set_addr(unsigned long iobase,
+ unsigned int sb_io,
+ unsigned int mpu_io,
+ unsigned int opl_io,
+ unsigned int game_io)
{
- u32 confA = 0;
- u32 confB = 0;
-
- if (mpu > 0)
- confB |= (mpu | 1) << 16;
- if (sb > 0)
- confB |= (sb | 1);
- if (game > 0)
- confA |= (game | 1) << 16;
- if (opl > 0)
- confA |= (opl | 1);
- snd_als4000_gcr_write_addr(gcr, 0xa8, confA);
- snd_als4000_gcr_write_addr(gcr, 0xa9, confB);
+ u32 cfg1 = 0;
+ u32 cfg2 = 0;
+
+ if (mpu_io > 0)
+ cfg2 |= (mpu_io | 1) << 16;
+ if (sb_io > 0)
+ cfg2 |= (sb_io | 1);
+ if (game_io > 0)
+ cfg1 |= (game_io | 1) << 16;
+ if (opl_io > 0)
+ cfg1 |= (opl_io | 1);
+ snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA8_LEGACY_CFG1, cfg1);
+ snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA9_LEGACY_CFG2, cfg2);
}
static void snd_als4000_configure(struct snd_sb *chip)
{
- unsigned tmp;
+ u8 tmp;
int i;
/* do some more configuration */
spin_lock_irq(&chip->mixer_lock);
- tmp = snd_sbmixer_read(chip, 0xc0);
- snd_sbmixer_write(chip, 0xc0, tmp|0x80);
- /* always select DMA channel 0, since we do not actually use DMA */
+ tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
+ snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+ tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
+ /* always select DMA channel 0, since we do not actually use DMA
+ * SPECS_PAGE: 19/20 */
snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
- snd_sbmixer_write(chip, 0xc0, tmp&0x7f);
+ snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+ tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
spin_unlock_irq(&chip->mixer_lock);
spin_lock_irq(&chip->reg_lock);
- /* magic number. Enables interrupts(?) */
- snd_als4000_gcr_write(chip, 0x8c, 0x28000);
- for(i = 0x91; i <= 0x96; ++i)
- snd_als4000_gcr_write(chip, i, 0);
+ /* enable interrupts */
+ snd_als4k_gcr_write(chip, ALS4K_GCR8C_MISC_CTRL,
+ ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE);
+
+ /* SPECS_PAGE: 39 */
+ for (i = ALS4K_GCR91_DMA0_ADDR; i <= ALS4K_GCR96_DMA3_MODE_COUNT; ++i)
+ snd_als4k_gcr_write(chip, i, 0);
- snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99));
+ snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL,
+ snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL));
spin_unlock_irq(&chip->reg_lock);
}
@@ -628,7 +810,7 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
gameport_set_port_data(gp, r);
/* Enable legacy joystick port */
- snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1);
+ snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
gameport_register_port(acard->gameport);
@@ -643,7 +825,9 @@ static void snd_als4000_free_gameport(struct snd_card_als4000 *acard)
gameport_unregister_port(acard->gameport);
acard->gameport = NULL;
- snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0); /* disable joystick */
+ /* disable joystick */
+ snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
+
release_and_free_resource(r);
}
}
@@ -654,10 +838,10 @@ static inline void snd_als4000_free_gameport(struct snd_card_als4000 *acard) { }
static void snd_card_als4000_free( struct snd_card *card )
{
- struct snd_card_als4000 * acard = (struct snd_card_als4000 *)card->private_data;
+ struct snd_card_als4000 *acard = card->private_data;
/* make sure that interrupts are disabled */
- snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0);
+ snd_als4k_gcr_write_addr(acard->iobase, ALS4K_GCR8C_MISC_CTRL, 0);
/* free resources */
snd_als4000_free_gameport(acard);
pci_release_regions(acard->pci);
@@ -670,7 +854,7 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
static int dev;
struct snd_card *card;
struct snd_card_als4000 *acard;
- unsigned long gcr;
+ unsigned long iobase;
struct snd_sb *chip;
struct snd_opl3 *opl3;
unsigned short word;
@@ -699,31 +883,32 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
pci_disable_device(pci);
return err;
}
- gcr = pci_resource_start(pci, 0);
+ iobase = pci_resource_start(pci, 0);
pci_read_config_word(pci, PCI_COMMAND, &word);
pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
pci_set_master(pci);
card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof( struct snd_card_als4000 ) );
+ sizeof(*acard) /* private_data: acard */);
if (card == NULL) {
pci_release_regions(pci);
pci_disable_device(pci);
return -ENOMEM;
}
- acard = (struct snd_card_als4000 *)card->private_data;
+ acard = card->private_data;
acard->pci = pci;
- acard->gcr = gcr;
+ acard->iobase = iobase;
card->private_free = snd_card_als4000_free;
/* disable all legacy ISA stuff */
- snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0);
+ snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
if ((err = snd_sbdsp_create(card,
- gcr + 0x10,
+ iobase + ALS4K_IOB_10_ADLIB_ADDR0,
pci->irq,
+ /* internally registered as IRQF_SHARED in case of ALS4000 SB */
snd_als4000_interrupt,
-1,
-1,
@@ -734,7 +919,7 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
acard->chip = chip;
chip->pci = pci;
- chip->alt_port = gcr;
+ chip->alt_port = iobase;
snd_card_set_dev(card, &pci->dev);
snd_als4000_configure(chip);
@@ -745,11 +930,18 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
card->shortname, chip->alt_port, chip->irq);
if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
- gcr+0x30, MPU401_INFO_INTEGRATED,
+ iobase + ALS4K_IOB_30_MIDI_DATA,
+ MPU401_INFO_INTEGRATED,
pci->irq, 0, &chip->rmidi)) < 0) {
- printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", gcr+0x30);
+ printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n",
+ iobase + ALS4K_IOB_30_MIDI_DATA);
goto out_err;
}
+ /* FIXME: ALS4000 has interesting MPU401 configuration features
+ * at ALS4K_CR1A_MPU401_UART_MODE_CONTROL
+ * (pass-thru / UART switching, fast MIDI clock, etc.),
+ * however there doesn't seem to be an ALSA API for this...
+ * SPECS_PAGE: 21 */
if ((err = snd_als4000_pcm(chip, 0)) < 0) {
goto out_err;
@@ -758,10 +950,13 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
goto out_err;
}
- if (snd_opl3_create(card, gcr+0x10, gcr+0x12,
+ if (snd_opl3_create(card,
+ iobase + ALS4K_IOB_10_ADLIB_ADDR0,
+ iobase + ALS4K_IOB_12_ADLIB_ADDR2,
OPL3_HW_AUTO, 1, &opl3) < 0) {
printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx?\n",
- gcr+0x10, gcr+0x12 );
+ iobase + ALS4K_IOB_10_ADLIB_ADDR0,
+ iobase + ALS4K_IOB_12_ADLIB_ADDR2);
} else {
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
goto out_err;
@@ -831,13 +1026,13 @@ static int snd_als4000_resume(struct pci_dev *pci)
#ifdef SUPPORT_JOYSTICK
if (acard->gameport)
- snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1);
+ snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
#endif
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
+#endif /* CONFIG_PM */
static struct pci_driver driver = {
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 457228fb22a..085a52b8c80 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -37,7 +37,7 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ATI IXP AC97 controller");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}");
+MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400/600}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -290,6 +290,7 @@ static struct pci_device_id snd_atiixp_ids[] = {
{ 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
{ 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */
{ 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
+ { 0x1002, 0x4382, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB600 */
{ 0, }
};
@@ -722,7 +723,9 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct atiixp_dma *dma = substream->runtime->private_data;
int err = 0;
- snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+ if (snd_BUG_ON(!dma->ops->enable_transfer ||
+ !dma->ops->flush_dma))
+ return -EINVAL;
spin_lock(&chip->reg_lock);
switch (cmd) {
@@ -1032,7 +1035,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+ if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+ return -EINVAL;
if (dma->opened)
return -EBUSY;
@@ -1064,7 +1068,8 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
/* disable DMA bits */
- snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+ if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+ return -EINVAL;
spin_lock_irq(&chip->reg_lock);
dma->ops->enable_dma(chip, 0);
spin_unlock_irq(&chip->reg_lock);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index d457a32a793..2f106306c7f 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -674,7 +674,9 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct atiixp_dma *dma = substream->runtime->private_data;
int err = 0;
- snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+ if (snd_BUG_ON(!dma->ops->enable_transfer ||
+ !dma->ops->flush_dma))
+ return -EINVAL;
spin_lock(&chip->reg_lock);
switch(cmd) {
@@ -865,7 +867,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
.mask = 0,
};
- snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+ if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+ return -EINVAL;
if (dma->opened)
return -EBUSY;
@@ -895,7 +898,8 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
{
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
/* disable DMA bits */
- snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+ if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+ return -EINVAL;
spin_lock_irq(&chip->reg_lock);
dma->ops->enable_dma(chip, 0);
spin_unlock_irq(&chip->reg_lock);
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index 4aad35bba11..cf46bba563c 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -125,7 +125,6 @@ typedef struct {
/* Virtual page extender stuff */
int nr_periods;
int period_bytes;
- struct snd_sg_buf *sgbuf; /* DMA Scatter Gather struct */
int period_real;
int period_virt;
@@ -195,16 +194,14 @@ static void vortex_adb_setsrc(vortex_t * vortex, int adbdma,
/* DMA Engines. */
static void vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
- struct snd_sg_buf * sgbuf, int size,
- int count);
+ int size, int count);
static void vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie,
int dir, int fmt, int d,
u32 offset);
static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb);
#ifndef CHIP_AU8810
static void vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
- struct snd_sg_buf * sgbuf, int size,
- int count);
+ int size, int count);
static void vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d, /*int e, */
u32 offset);
static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb);
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 333c62de862..b070e571451 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -427,7 +427,7 @@ static void vortex_mixer_init(vortex_t * vortex)
/* Set clipping ceiling (this may be all wrong). */
/*
- for (x = 0; x > 0x80; x++) {
+ for (x = 0; x < 0x80; x++) {
hwwrite(vortex->mmio, VORTEX_MIXER_CLIP + (x << 2), 0x3ffff);
}
*/
@@ -1097,19 +1097,12 @@ static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb)
static void
vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
- struct snd_sg_buf * sgbuf, int psize, int count)
+ int psize, int count)
{
stream_t *dma = &vortex->dma_adb[adbdma];
- if (sgbuf == NULL) {
- printk(KERN_INFO "vortex: FATAL: sgbuf is NULL!\n");
- return;
- }
- //printk(KERN_INFO "vortex: page count = %d, tblcount = %d\n", count, sgbuf->tblsize);
-
dma->period_bytes = psize;
dma->nr_periods = count;
- dma->sgbuf = sgbuf;
dma->cfg0 = 0;
dma->cfg1 = 0;
@@ -1120,26 +1113,26 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize - 1);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc,
- snd_sgbuf_get_addr(sgbuf, psize * 3));
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
/* 3 pages */
case 3:
dma->cfg0 |= 0x12000000;
dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
- snd_sgbuf_get_addr(sgbuf, psize * 2));
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
/* 2 pages */
case 2:
dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
- snd_sgbuf_get_addr(sgbuf, psize));
+ snd_pcm_sgbuf_get_addr(dma->substream, psize));
/* 1 page */
case 1:
dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4),
- snd_sgbuf_get_addr(sgbuf, 0));
+ snd_pcm_sgbuf_get_addr(dma->substream, 0));
break;
}
//printk("vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1);
@@ -1205,7 +1198,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
//hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), dma->table[p].addr);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
- snd_sgbuf_get_addr(dma->sgbuf,
+ snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
/* Force write thru cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
@@ -1244,7 +1237,10 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
if (pp >= 4)
pp -= 4;
}
- hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
+ snd_pcm_sgbuf_get_addr(dma->substream,
+ dma->period_bytes * p));
/* Force write thru cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
}
@@ -1367,13 +1363,12 @@ static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb)
static void
vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
- struct snd_sg_buf * sgbuf, int psize, int count)
+ int psize, int count)
{
stream_t *dma = &vortex->dma_wt[wtdma];
dma->period_bytes = psize;
dma->nr_periods = count;
- dma->sgbuf = sgbuf;
dma->cfg0 = 0;
dma->cfg1 = 0;
@@ -1383,23 +1378,23 @@ vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
case 4:
dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
- snd_sgbuf_get_addr(sgbuf, psize * 3));
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
/* 3 pages */
case 3:
dma->cfg0 |= 0x12000000;
dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x8,
- snd_sgbuf_get_addr(sgbuf, psize * 2));
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
/* 2 pages */
case 2:
dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
- snd_sgbuf_get_addr(sgbuf, psize));
+ snd_pcm_sgbuf_get_addr(dma->substream, psize));
/* 1 page */
case 1:
dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
- snd_sgbuf_get_addr(sgbuf, 0));
+ snd_pcm_sgbuf_get_addr(dma->substream, 0));
break;
}
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + (wtdma << 3), dma->cfg0);
@@ -1465,7 +1460,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
hwwrite(vortex->mmio,
VORTEX_WTDMA_BUFBASE +
(((wtdma << 2) + pp) << 2),
- snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+ snd_pcm_sgbuf_get_addr(dma->substream,
+ dma->period_bytes * p));
/* Force write thru cache. */
hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
(((wtdma << 2) + pp) << 2));
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index bc212f41a38..e291aa59742 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -1,6 +1,4 @@
/*
- * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $
- *
* Manuel Jander.
*
* Based on the work of:
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index f9a58b4a30e..b9d2f202cf9 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -189,7 +189,6 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
{
vortex_t *chip = snd_pcm_substream_chip(substream);
stream_t *stream = (stream_t *) (substream->runtime->private_data);
- struct snd_sg_buf *sgbuf;
int err;
// Alloc buffer memory.
@@ -199,8 +198,6 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
return err;
}
- //sgbuf = (struct snd_sg_buf *) substream->runtime->dma_private;
- sgbuf = snd_pcm_substream_sgbuf(substream);
/*
printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
params_period_bytes(hw_params), params_channels(hw_params));
@@ -226,7 +223,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
stream = substream->runtime->private_data = &chip->dma_adb[dma];
stream->substream = substream;
/* Setup Buffers. */
- vortex_adbdma_setbuffers(chip, dma, sgbuf,
+ vortex_adbdma_setbuffers(chip, dma,
params_period_bytes(hw_params),
params_periods(hw_params));
}
@@ -240,7 +237,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
&chip->dma_wt[substream->number];
stream->dma = substream->number;
stream->substream = substream;
- vortex_wtdma_setbuffers(chip, substream->number, sgbuf,
+ vortex_wtdma_setbuffers(chip, substream->number,
params_period_bytes(hw_params),
params_periods(hw_params));
}
@@ -392,13 +389,6 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
return (bytes_to_frames(substream->runtime, current_ptr));
}
-/* Page callback. */
-/*
-static struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset) {
-
-
-}
-*/
/* operators */
static struct snd_pcm_ops snd_vortex_playback_ops = {
.open = snd_vortex_pcm_open,
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 5f63af6b88a..333007c523a 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1,6 +1,6 @@
/*
* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- * Copyright (C) 2002, 2005, 2006, 2007 by Andreas Mohr <andi AT lisas.de>
+ * Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de>
*
* Framework borrowed from Bart Hartgers's als4000.c.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -35,9 +35,20 @@
* (3 weeks' worth of evenings filled with driver work).
* (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
*
+ * It is quite likely that the AZF3328 chip is the PCI cousin of the
+ * AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
+ *
* The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
- * for compatibility reasons) has the following features:
+ * for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
+ * Fincitec acquired by National Semiconductor in 2002, together with the
+ * Fincitec-related company ARSmikro) has the following features:
*
+ * - compatibility & compliance:
+ * - Microsoft PC 97 ("PC 97 Hardware Design Guide",
+ * http://www.microsoft.com/whdc/archive/pcguides.mspx)
+ * - Microsoft PC 98 Baseline Audio
+ * - MPU401 UART
+ * - Sound Blaster Emulation (DOS Box)
* - builtin AC97 conformant codec (SNR over 80dB)
* Note that "conformant" != "compliant"!! this chip's mixer register layout
* *differs* from the standard AC97 layout:
@@ -48,21 +59,28 @@
* addresses illegally. So far unfortunately it looks like the very flexible
* ALSA AC97 support is still not enough to easily compensate for such a
* grave layout violation despite all tweaks and quirks mechanisms it offers.
- * - builtin genuine OPL3
+ * - builtin genuine OPL3 - verified to work fine, 20080506
* - full duplex 16bit playback/record at independent sampling rate
- * - MPU401 (+ legacy address support) FIXME: how to enable legacy addr??
+ * - MPU401 (+ legacy address support, claimed by one official spec sheet)
+ * FIXME: how to enable legacy addr??
* - game port (legacy address support)
- * - builtin 3D enhancement (said to be YAMAHA Ymersion)
* - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
- * features supported)
+ * features supported). - See common term "Digital Enhanced Game Port"...
+ * (probably DirectInput 3.0 spec - confirm)
+ * - builtin 3D enhancement (said to be YAMAHA Ymersion)
* - built-in General DirectX timer having a 20 bits counter
* with 1us resolution (see below!)
- * - I2S serial port for external DAC
+ * - I2S serial output port for external DAC
* - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
* - supports hardware volume control
* - single chip low cost solution (128 pin QFP)
* - supports programmable Sub-vendor and Sub-system ID
* required for Microsoft's logo compliance (FIXME: where?)
+ * At least the Trident 4D Wave DX has one bit somewhere
+ * to enable writes to PCI subsystem VID registers, that should be it.
+ * This might easily be in extended PCI reg space, since PCI168 also has
+ * some custom data starting at 0x80. What kind of config settings
+ * are located in our extended PCI space anyway??
* - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
*
* Note that this driver now is actually *better* than the Windows driver,
@@ -74,6 +92,24 @@
* - "timidity -iAv -B2,8 -Os -EFreverb=0"
* - "pmidi -p 128:0 jazz.mid"
*
+ * OPL3 hardware playback testing, try something like:
+ * cat /proc/asound/hwdep
+ * and
+ * aconnect -o
+ * Then use
+ * sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
+ * where x,y is the xx-yy number as given in hwdep.
+ * Then try
+ * pmidi -p a:b jazz.mid
+ * where a:b is the client number plus 0 usually, as given by aconnect above.
+ * Oh, and make sure to unmute the FM mixer control (doh!)
+ * NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
+ * despite no CPU activity, possibly due to hindering ACPI idling somehow.
+ * Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
+ * Higher PCM / FM mixer levels seem to conflict (causes crackling),
+ * at least sometimes. Maybe even use with hardware sequencer timer above :)
+ * adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
+ *
* Certain PCI versions of this card are susceptible to DMA traffic underruns
* in some systems (resulting in sound crackling/clicking/popping),
* probably because they don't have a DMA FIFO buffer or so.
@@ -87,6 +123,8 @@
* better than a VIA, yet ironically I still get crackling, like many other
* people with the same chipset.
* Possible remedies:
+ * - use speaker (amplifier) output instead of headphone output
+ * (in case crackling is due to overloaded output clipping)
* - plug card into a different PCI slot, preferrably one that isn't shared
* too much (this helps a lot, but not completely!)
* - get rid of PCI VGA card, use AGP instead
@@ -94,18 +132,23 @@
* - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
* Not too helpful.
* - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
- *
+ *
* BUGS
- * - full-duplex might *still* be problematic, not fully tested recently
+ * - full-duplex might *still* be problematic, however a recent test was fine
* - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
* if you set PCM output switch to "pre 3D" instead of "post 3D".
* If this can't be set, then get a mixer application that Isn't Stupid (tm)
* (e.g. kmix, gamix) - unfortunately several are!!
- *
+ * - locking is not entirely clean, especially the audio stream activity
+ * ints --> may be racy
+ * - an _unconnected_ secondary joystick at the gameport will be reported
+ * to be "active" (floating values, not precisely -1) due to the way we need
+ * to read the Digital Enhanced Game Port. Not sure whether it is fixable.
+ *
* TODO
* - test MPU401 MIDI playback etc.
- * - add some power micro-management (disable various units of the card
- * as long as they're unused). However this requires I/O ports which I
+ * - add more power micro-management (disable various units of the card
+ * as long as they're unused). However this requires more I/O ports which I
* haven't figured out yet and which thus might not even exist...
* The standard suspend/resume functionality could probably make use of
* some improvement, too...
@@ -113,6 +156,7 @@
* - figure out some cleverly evil scheme to possibly make ALSA AC97 code
* fully accept our quite incompatible ""AC97"" mixer and thus save some
* code (but I'm not too optimistic that doing this is possible at all)
+ * - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
*/
#include <asm/io.h>
@@ -138,7 +182,7 @@ MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
-#define SUPPORT_JOYSTICK 1
+#define SUPPORT_GAMEPORT 1
#endif
#define DEBUG_MISC 0
@@ -147,13 +191,14 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define DEBUG_PLAY_REC 0
#define DEBUG_IO 0
#define DEBUG_TIMER 0
+#define DEBUG_GAME 0
#define MIXER_TESTING 0
#if DEBUG_MISC
#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
#else
#define snd_azf3328_dbgmisc(format, args...)
-#endif
+#endif
#if DEBUG_CALLS
#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
@@ -163,25 +208,31 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define snd_azf3328_dbgcalls(format, args...)
#define snd_azf3328_dbgcallenter()
#define snd_azf3328_dbgcallleave()
-#endif
+#endif
#if DEBUG_MIXER
#define snd_azf3328_dbgmixer(format, args...) printk(format, ##args)
#else
#define snd_azf3328_dbgmixer(format, args...)
-#endif
+#endif
#if DEBUG_PLAY_REC
#define snd_azf3328_dbgplay(format, args...) printk(KERN_ERR format, ##args)
#else
#define snd_azf3328_dbgplay(format, args...)
-#endif
+#endif
#if DEBUG_MISC
#define snd_azf3328_dbgtimer(format, args...) printk(KERN_ERR format, ##args)
#else
#define snd_azf3328_dbgtimer(format, args...)
-#endif
+#endif
+
+#if DEBUG_GAME
+#define snd_azf3328_dbggame(format, args...) printk(KERN_ERR format, ##args)
+#else
+#define snd_azf3328_dbggame(format, args...)
+#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
module_param_array(index, int, NULL, 0444);
@@ -195,51 +246,62 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
-#ifdef SUPPORT_JOYSTICK
-static int joystick[SNDRV_CARDS];
-module_param_array(joystick, bool, NULL, 0444);
-MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard.");
-#endif
-
static int seqtimer_scaling = 128;
module_param(seqtimer_scaling, int, 0444);
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
+struct snd_azf3328_audio_stream {
+ struct snd_pcm_substream *substream;
+ int enabled;
+ int running;
+ unsigned long portbase;
+};
+
+enum snd_azf3328_stream_index {
+ AZF_PLAYBACK = 0,
+ AZF_CAPTURE = 1,
+};
+
struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */
- unsigned long codec_port;
- unsigned long io2_port;
- unsigned long mpu_port;
- unsigned long synth_port;
- unsigned long mixer_port;
+
+ unsigned long codec_io; /* usually 0xb000, size 128 */
+ unsigned long game_io; /* usually 0xb400, size 8 */
+ unsigned long mpu_io; /* usually 0xb800, size 4 */
+ unsigned long opl3_io; /* usually 0xbc00, size 8 */
+ unsigned long mixer_io; /* usually 0xc000, size 64 */
spinlock_t reg_lock;
struct snd_timer *timer;
-
+
struct snd_pcm *pcm;
- struct snd_pcm_substream *playback_substream;
- struct snd_pcm_substream *capture_substream;
- unsigned int is_playing;
- unsigned int is_recording;
+ struct snd_azf3328_audio_stream audio_stream[2];
struct snd_card *card;
struct snd_rawmidi *rmidi;
-#ifdef SUPPORT_JOYSTICK
+#ifdef SUPPORT_GAMEPORT
struct gameport *gameport;
+ int axes[4];
#endif
struct pci_dev *pci;
int irq;
+ /* register 0x6a is write-only, thus need to remember setting.
+ * If we need to add more registers here, then we might try to fold this
+ * into some transparent combined shadow register handling with
+ * CONFIG_PM register storage below, but that's slightly difficult. */
+ u16 shadow_reg_codec_6AH;
+
#ifdef CONFIG_PM
/* register value containers for power management
* Note: not always full I/O range preserved (just like Win driver!) */
- u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2];
- u16 saved_regs_io2 [AZF_IO_SIZE_IO2_PM / 2];
- u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
- u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2];
+ u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2];
+ u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
+ u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
+ u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
#endif
};
@@ -252,126 +314,166 @@ static const struct pci_device_id snd_azf3328_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
+
+static int
+snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
+{
+ u8 prev = inb(reg), new;
+
+ new = (do_set) ? (prev|mask) : (prev & ~mask);
+ /* we need to always write the new value no matter whether it differs
+ * or not, since some register bits don't indicate their setting */
+ outb(new, reg);
+ if (new != prev)
+ return 1;
+
+ return 0;
+}
+
static inline void
-snd_azf3328_codec_outb(const struct snd_azf3328 *chip, int reg, u8 value)
+snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
{
- outb(value, chip->codec_port + reg);
+ outb(value, chip->codec_io + reg);
}
static inline u8
-snd_azf3328_codec_inb(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg)
{
- return inb(chip->codec_port + reg);
+ return inb(chip->codec_io + reg);
}
static inline void
-snd_azf3328_codec_outw(const struct snd_azf3328 *chip, int reg, u16 value)
+snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
- outw(value, chip->codec_port + reg);
+ outw(value, chip->codec_io + reg);
}
static inline u16
-snd_azf3328_codec_inw(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+ return inw(chip->codec_io + reg);
+}
+
+static inline void
+snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
+{
+ outl(value, chip->codec_io + reg);
+}
+
+static inline u32
+snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg)
{
- return inw(chip->codec_port + reg);
+ return inl(chip->codec_io + reg);
}
static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328 *chip, int reg, u32 value)
+snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
{
- outl(value, chip->codec_port + reg);
+ outb(value, chip->game_io + reg);
}
static inline void
-snd_azf3328_io2_outb(const struct snd_azf3328 *chip, int reg, u8 value)
+snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
- outb(value, chip->io2_port + reg);
+ outw(value, chip->game_io + reg);
}
static inline u8
-snd_azf3328_io2_inb(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
{
- return inb(chip->io2_port + reg);
+ return inb(chip->game_io + reg);
+}
+
+static inline u16
+snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+ return inw(chip->game_io + reg);
}
static inline void
-snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, int reg, u16 value)
+snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
- outw(value, chip->mixer_port + reg);
+ outw(value, chip->mixer_io + reg);
}
static inline u16
-snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
{
- return inw(chip->mixer_port + reg);
+ return inw(chip->mixer_io + reg);
}
-static void
-snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, int reg, int do_mute)
+#define AZF_MUTE_BIT 0x80
+
+static int
+snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
+ unsigned reg, int do_mute
+)
{
- unsigned long portbase = chip->mixer_port + reg + 1;
- unsigned char oldval;
+ unsigned long portbase = chip->mixer_io + reg + 1;
+ int updated;
/* the mute bit is on the *second* (i.e. right) register of a
* left/right channel setting */
- oldval = inb(portbase);
- if (do_mute)
- oldval |= 0x80;
- else
- oldval &= ~0x80;
- outb(oldval, portbase);
+ updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
+
+ /* indicate whether it was muted before */
+ return (do_mute) ? !updated : updated;
}
static void
-snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg, unsigned char dst_vol_left, unsigned char dst_vol_right, int chan_sel, int delay)
+snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
+ unsigned reg,
+ unsigned char dst_vol_left,
+ unsigned char dst_vol_right,
+ int chan_sel, int delay
+)
{
- unsigned long portbase = chip->mixer_port + reg;
+ unsigned long portbase = chip->mixer_io + reg;
unsigned char curr_vol_left = 0, curr_vol_right = 0;
- int left_done = 0, right_done = 0;
-
+ int left_change = 0, right_change = 0;
+
snd_azf3328_dbgcallenter();
- if (chan_sel & SET_CHAN_LEFT)
+
+ if (chan_sel & SET_CHAN_LEFT) {
curr_vol_left = inb(portbase + 1);
- else
- left_done = 1;
- if (chan_sel & SET_CHAN_RIGHT)
+
+ /* take care of muting flag contained in left channel */
+ if (curr_vol_left & AZF_MUTE_BIT)
+ dst_vol_left |= AZF_MUTE_BIT;
+ else
+ dst_vol_left &= ~AZF_MUTE_BIT;
+
+ left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
+ }
+
+ if (chan_sel & SET_CHAN_RIGHT) {
curr_vol_right = inb(portbase + 0);
- else
- right_done = 1;
-
- /* take care of muting flag (0x80) contained in left channel */
- if (curr_vol_left & 0x80)
- dst_vol_left |= 0x80;
- else
- dst_vol_left &= ~0x80;
+
+ right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
+ }
do {
- if (!left_done) {
- if (curr_vol_left > dst_vol_left)
- curr_vol_left--;
- else
- if (curr_vol_left < dst_vol_left)
- curr_vol_left++;
- else
- left_done = 1;
- outb(curr_vol_left, portbase + 1);
+ if (left_change) {
+ if (curr_vol_left != dst_vol_left) {
+ curr_vol_left += left_change;
+ outb(curr_vol_left, portbase + 1);
+ } else
+ left_change = 0;
}
- if (!right_done) {
- if (curr_vol_right > dst_vol_right)
- curr_vol_right--;
- else
- if (curr_vol_right < dst_vol_right)
- curr_vol_right++;
- else
- right_done = 1;
+ if (right_change) {
+ if (curr_vol_right != dst_vol_right) {
+ curr_vol_right += right_change;
+
/* during volume change, the right channel is crackling
* somewhat more than the left channel, unfortunately.
* This seems to be a hardware issue. */
- outb(curr_vol_right, portbase + 0);
+ outb(curr_vol_right, portbase + 0);
+ } else
+ right_change = 0;
}
if (delay)
mdelay(delay);
- } while ((!left_done) || (!right_done));
+ } while ((left_change) || (right_change));
snd_azf3328_dbgcallleave();
}
@@ -379,7 +481,7 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg
* general mixer element
*/
struct azf3328_mixer_reg {
- unsigned int reg;
+ unsigned reg;
unsigned int lchan_shift, rchan_shift;
unsigned int mask;
unsigned int invert: 1;
@@ -544,13 +646,14 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
"Mix", "Mic"
};
static const char * const texts3[] = {
- "Mic", "CD", "Video", "Aux",
+ "Mic", "CD", "Video", "Aux",
"Line", "Mix", "Mix Mono", "Phone"
};
static const char * const texts4[] = {
"pre 3D", "post 3D"
};
struct azf3328_mixer_reg reg;
+ const char * const *p = NULL;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -561,18 +664,20 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
if (reg.reg == IDX_MIXER_ADVCTL2) {
switch(reg.lchan_shift) {
case 8: /* modem out sel */
- strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]);
+ p = texts1;
break;
case 9: /* mono sel source */
- strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]);
+ p = texts2;
break;
case 15: /* PCM Out Path */
- strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]);
+ p = texts4;
break;
}
} else
- strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]
-);
+ if (reg.reg == IDX_MIXER_REC_SELECT)
+ p = texts3;
+
+ strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
return 0;
}
@@ -583,7 +688,7 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
unsigned short val;
-
+
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
val = snd_azf3328_mixer_inw(chip, reg.reg);
if (reg.reg == IDX_MIXER_REC_SELECT) {
@@ -605,7 +710,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
unsigned int oreg, nreg, val;
-
+
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
val = oreg;
@@ -631,9 +736,11 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
- AZF3328_MIXER_SWITCH("Wave Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
- AZF3328_MIXER_VOL_STEREO("Wave Playback Volume", IDX_MIXER_WAVEOUT, 0x1f, 1),
- AZF3328_MIXER_SWITCH("Wave 3D Bypass Playback Switch", IDX_MIXER_ADVCTL2, 7, 1),
+ AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
+ AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
+ IDX_MIXER_WAVEOUT, 0x1f, 1),
+ AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
+ IDX_MIXER_ADVCTL2, 7, 1),
AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
@@ -709,7 +816,8 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
int err;
snd_azf3328_dbgcallenter();
- snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip || !chip->card))
+ return -EINVAL;
card = chip->card;
@@ -717,15 +825,16 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
/* mute and zero volume channels */
- for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); idx++) {
+ for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
snd_azf3328_mixer_outw(chip,
snd_azf3328_init_values[idx][0],
snd_azf3328_init_values[idx][1]);
}
-
+
/* add mixer controls */
sw = snd_azf3328_mixer_controls;
- for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls); idx++, sw++) {
+ for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
+ ++idx, ++sw) {
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
return err;
}
@@ -757,9 +866,9 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
}
static void
-snd_azf3328_setfmt(struct snd_azf3328 *chip,
- unsigned int reg,
- unsigned int bitrate,
+snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
+ unsigned reg,
+ enum azf_freq_t bitrate,
unsigned int format_width,
unsigned int channels
)
@@ -769,24 +878,25 @@ snd_azf3328_setfmt(struct snd_azf3328 *chip,
snd_azf3328_dbgcallenter();
switch (bitrate) {
- case 4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
- case 4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
- case 5512: val |= SOUNDFORMAT_FREQ_5510; break; /* the AZF3328 names it "5510" for some strange reason */
- case 6620: val |= SOUNDFORMAT_FREQ_6620; break;
- case 8000: val |= SOUNDFORMAT_FREQ_8000; break;
- case 9600: val |= SOUNDFORMAT_FREQ_9600; break;
- case 11025: val |= SOUNDFORMAT_FREQ_11025; break;
- case 13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
- case 16000: val |= SOUNDFORMAT_FREQ_16000; break;
- case 22050: val |= SOUNDFORMAT_FREQ_22050; break;
- case 32000: val |= SOUNDFORMAT_FREQ_32000; break;
- case 44100: val |= SOUNDFORMAT_FREQ_44100; break;
- case 48000: val |= SOUNDFORMAT_FREQ_48000; break;
- case 66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
+ case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
+ case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
+ case AZF_FREQ_5512:
+ /* the AZF3328 names it "5510" for some strange reason */
+ val |= SOUNDFORMAT_FREQ_5510; break;
+ case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break;
+ case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break;
+ case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break;
+ case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
+ case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
+ case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
+ case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
+ case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
default:
snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
- val |= SOUNDFORMAT_FREQ_44100;
- break;
+ /* fall-through */
+ case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
+ case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
+ case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
}
/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
@@ -805,10 +915,10 @@ snd_azf3328_setfmt(struct snd_azf3328 *chip,
val |= SOUNDFORMAT_FLAG_16BIT;
spin_lock_irqsave(&chip->reg_lock, flags);
-
+
/* set bitrate/format */
snd_azf3328_codec_outw(chip, reg, val);
-
+
/* changing the bitrate/format settings switches off the
* audio output with an annoying click in case of 8/16bit format change
* (maybe shutting down DAC/ADC?), thus immediately
@@ -830,31 +940,95 @@ snd_azf3328_setfmt(struct snd_azf3328 *chip,
snd_azf3328_dbgcallleave();
}
+static inline void
+snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
+ unsigned reg
+)
+{
+ /* choose lowest frequency for low power consumption.
+ * While this will cause louder noise due to rather coarse frequency,
+ * it should never matter since output should always
+ * get disabled properly when idle anyway. */
+ snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1);
+}
+
+static void
+snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip,
+ unsigned bitmask,
+ int enable
+)
+{
+ if (enable)
+ chip->shadow_reg_codec_6AH &= ~bitmask;
+ else
+ chip->shadow_reg_codec_6AH |= bitmask;
+ snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
+ bitmask, enable, chip->shadow_reg_codec_6AH);
+ snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH);
+}
+
+static inline void
+snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable)
+{
+ snd_azf3328_dbgplay("codec_enable %d\n", enable);
+ /* no idea what exactly is being done here, but I strongly assume it's
+ * PM related */
+ snd_azf3328_codec_reg_6AH_update(
+ chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
+ );
+}
+
+static void
+snd_azf3328_codec_activity(struct snd_azf3328 *chip,
+ enum snd_azf3328_stream_index stream_type,
+ int enable
+)
+{
+ int need_change = (chip->audio_stream[stream_type].running != enable);
+
+ snd_azf3328_dbgplay(
+ "codec_activity: type %d, enable %d, need_change %d\n",
+ stream_type, enable, need_change
+ );
+ if (need_change) {
+ enum snd_azf3328_stream_index other =
+ (stream_type == AZF_PLAYBACK) ?
+ AZF_CAPTURE : AZF_PLAYBACK;
+ /* small check to prevent shutting down the other party
+ * in case it's active */
+ if ((enable) || !(chip->audio_stream[other].running))
+ snd_azf3328_codec_enable(chip, enable);
+
+ /* ...and adjust clock, too
+ * (reduce noise and power consumption) */
+ if (!enable)
+ snd_azf3328_codec_setfmt_lowpower(
+ chip,
+ chip->audio_stream[stream_type].portbase
+ + IDX_IO_PLAY_SOUNDFORMAT
+ );
+ }
+ chip->audio_stream[stream_type].running = enable;
+}
+
static void
snd_azf3328_setdmaa(struct snd_azf3328 *chip,
long unsigned int addr,
unsigned int count,
unsigned int size,
- int do_recording)
+ enum snd_azf3328_stream_index stream_type
+)
{
- unsigned long flags, portbase;
- unsigned int is_running;
-
snd_azf3328_dbgcallenter();
- if (do_recording) {
- /* access capture registers, i.e. skip playback reg section */
- portbase = chip->codec_port + 0x20;
- is_running = chip->is_recording;
- } else {
- /* access the playback register section */
- portbase = chip->codec_port + 0x00;
- is_running = chip->is_playing;
- }
+ if (!chip->audio_stream[stream_type].running) {
+ /* AZF3328 uses a two buffer pointer DMA playback approach */
- /* AZF3328 uses a two buffer pointer DMA playback approach */
- if (!is_running) {
- unsigned long addr_area2;
- unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */
+ unsigned long flags, portbase, addr_area2;
+
+ /* width 32bit (prevent overflow): */
+ unsigned long count_areas, count_tmp;
+
+ portbase = chip->audio_stream[stream_type].portbase;
count_areas = size/2;
addr_area2 = addr+count_areas;
count_areas--; /* max. index */
@@ -884,11 +1058,11 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
snd_azf3328_dbgcallenter();
#if 0
- snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+ snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 0);
+ snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK);
#endif
snd_azf3328_dbgcallleave();
return 0;
@@ -906,11 +1080,11 @@ snd_azf3328_capture_prepare(struct snd_pcm_substream *substream)
snd_azf3328_dbgcallenter();
#if 0
- snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
+ snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 1);
+ snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE);
#endif
snd_azf3328_dbgcallleave();
return 0;
@@ -923,6 +1097,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_runtime *runtime = substream->runtime;
int result = 0;
unsigned int status1;
+ int previously_muted;
snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd);
@@ -930,20 +1105,23 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
snd_azf3328_dbgplay("START PLAYBACK\n");
- /* mute WaveOut */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+ /* mute WaveOut (avoid clicking during setup) */
+ previously_muted =
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
- snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+ snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
spin_lock(&chip->reg_lock);
- /* stop playback */
+ /* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+
+ /* stop playback */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
-
+
/* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff);
spin_unlock(&chip->reg_lock);
@@ -951,7 +1129,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
snd_azf3328_setdmaa(chip, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream),
- 0);
+ AZF_PLAYBACK);
spin_lock(&chip->reg_lock);
#ifdef WIN9X
@@ -978,30 +1156,35 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
DMA_SOMETHING_ELSE);
#endif
spin_unlock(&chip->reg_lock);
+ snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1);
/* now unmute WaveOut */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+ if (!previously_muted)
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
- chip->is_playing = 1;
snd_azf3328_dbgplay("STARTED PLAYBACK\n");
break;
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME PLAYBACK\n");
/* resume playback if we were active */
- if (chip->is_playing)
+ spin_lock(&chip->reg_lock);
+ if (chip->audio_stream[AZF_PLAYBACK].running)
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
+ spin_unlock(&chip->reg_lock);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP PLAYBACK\n");
- /* mute WaveOut */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+ /* mute WaveOut (avoid clicking during setup) */
+ previously_muted =
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
spin_lock(&chip->reg_lock);
- /* stop playback */
+ /* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+ /* stop playback */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
@@ -1013,10 +1196,12 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
status1 &= ~DMA_PLAY_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
spin_unlock(&chip->reg_lock);
-
+ snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
+
/* now unmute WaveOut */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
- chip->is_playing = 0;
+ if (!previously_muted)
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+
snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1035,7 +1220,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
-
+
snd_azf3328_dbgcallleave();
return result;
}
@@ -1057,17 +1242,19 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
snd_azf3328_dbgplay("START CAPTURE\n");
- snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
+ snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
spin_lock(&chip->reg_lock);
- /* stop recording */
+ /* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
+
+ /* stop recording */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
+
/* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff);
spin_unlock(&chip->reg_lock);
@@ -1075,7 +1262,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
snd_azf3328_setdmaa(chip, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream),
- 1);
+ AZF_CAPTURE);
spin_lock(&chip->reg_lock);
#ifdef WIN9X
@@ -1102,24 +1289,27 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
DMA_SOMETHING_ELSE);
#endif
spin_unlock(&chip->reg_lock);
+ snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1);
- chip->is_recording = 1;
snd_azf3328_dbgplay("STARTED CAPTURE\n");
break;
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME CAPTURE\n");
/* resume recording if we were active */
- if (chip->is_recording)
+ spin_lock(&chip->reg_lock);
+ if (chip->audio_stream[AZF_CAPTURE].running)
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
+ spin_unlock(&chip->reg_lock);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP CAPTURE\n");
spin_lock(&chip->reg_lock);
- /* stop recording */
+ /* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
+ /* stop recording */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
@@ -1129,8 +1319,8 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
status1 &= ~DMA_PLAY_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
spin_unlock(&chip->reg_lock);
-
- chip->is_recording = 0;
+ snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0);
+
snd_azf3328_dbgplay("STOPPED CAPTURE\n");
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1149,7 +1339,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
-
+
snd_azf3328_dbgcallleave();
return result;
}
@@ -1162,11 +1352,11 @@ snd_azf3328_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frmres;
#ifdef QUERY_HARDWARE
- bufptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_START_1);
+ bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1);
#else
bufptr = substream->runtime->dma_addr;
#endif
- result = inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS);
+ result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS);
/* calculate offset */
result -= bufptr;
@@ -1183,11 +1373,11 @@ snd_azf3328_capture_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frmres;
#ifdef QUERY_HARDWARE
- bufptr = inl(chip->codec_port+IDX_IO_REC_DMA_START_1);
+ bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1);
#else
bufptr = substream->runtime->dma_addr;
#endif
- result = inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS);
+ result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
/* calculate offset */
result -= bufptr;
@@ -1196,27 +1386,242 @@ snd_azf3328_capture_pointer(struct snd_pcm_substream *substream)
return frmres;
}
+/******************************************************************/
+
+#ifdef SUPPORT_GAMEPORT
+static inline void
+snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
+{
+ snd_azf3328_io_reg_setb(
+ chip->game_io+IDX_GAME_HWCONFIG,
+ GAME_HWCFG_IRQ_ENABLE,
+ enable
+ );
+}
+
+static inline void
+snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
+{
+ snd_azf3328_io_reg_setb(
+ chip->game_io+IDX_GAME_HWCONFIG,
+ GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
+ enable
+ );
+}
+
+static inline void
+snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable)
+{
+ snd_azf3328_codec_reg_6AH_update(
+ chip, IO_6A_SOMETHING2_GAMEPORT, enable
+ );
+}
+
+static inline void
+snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
+{
+ /*
+ * skeleton handler only
+ * (we do not want axis reading in interrupt handler - too much load!)
+ */
+ snd_azf3328_dbggame("gameport irq\n");
+
+ /* this should ACK the gameport IRQ properly, hopefully. */
+ snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
+}
+
+static int
+snd_azf3328_gameport_open(struct gameport *gameport, int mode)
+{
+ struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+ int res;
+
+ snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ case GAMEPORT_MODE_RAW:
+ res = 0;
+ break;
+ default:
+ res = -1;
+ break;
+ }
+
+ snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
+
+ return res;
+}
+
+static void
+snd_azf3328_gameport_close(struct gameport *gameport)
+{
+ struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+
+ snd_azf3328_dbggame("gameport_close\n");
+ snd_azf3328_gameport_axis_circuit_enable(chip, 0);
+}
+
+static int
+snd_azf3328_gameport_cooked_read(struct gameport *gameport,
+ int *axes,
+ int *buttons
+)
+{
+ struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+ int i;
+ u8 val;
+ unsigned long flags;
+
+ if (snd_BUG_ON(!chip))
+ return 0;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
+ *buttons = (~(val) >> 4) & 0xf;
+
+ /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
+ * thus we're atomic and cannot actively wait in here
+ * (which would be useful for us since it probably would be better
+ * to trigger a measurement in here, then wait a short amount of
+ * time until it's finished, then read values of _this_ measurement).
+ *
+ * Thus we simply resort to reading values if they're available already
+ * and trigger the next measurement.
+ */
+
+ val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
+ if (val & GAME_AXES_SAMPLING_READY) {
+ for (i = 0; i < 4; ++i) {
+ /* configure the axis to read */
+ val = (i << 4) | 0x0f;
+ snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
+
+ chip->axes[i] = snd_azf3328_game_inw(
+ chip, IDX_GAME_AXIS_VALUE
+ );
+ }
+ }
+
+ /* trigger next axes sampling, to be evaluated the next time we
+ * enter this function */
+
+ /* for some very, very strange reason we cannot enable
+ * Measurement Ready monitoring for all axes here,
+ * at least not when only one joystick connected */
+ val = 0x03; /* we're able to monitor axes 1 and 2 only */
+ snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
+
+ snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ for (i = 0; i < 4; i++) {
+ axes[i] = chip->axes[i];
+ if (axes[i] == 0xffff)
+ axes[i] = -1;
+ }
+
+ snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
+ axes[0], axes[1], axes[2], axes[3], *buttons
+ );
+
+ return 0;
+}
+
+static int __devinit
+snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
+{
+ struct gameport *gp;
+
+ chip->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "AZF3328 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+ gameport_set_dev_parent(gp, &chip->pci->dev);
+ gp->io = chip->game_io;
+ gameport_set_port_data(gp, chip);
+
+ gp->open = snd_azf3328_gameport_open;
+ gp->close = snd_azf3328_gameport_close;
+ gp->fuzz = 16; /* seems ok */
+ gp->cooked_read = snd_azf3328_gameport_cooked_read;
+
+ /* DISABLE legacy address: we don't need it! */
+ snd_azf3328_gameport_legacy_address_enable(chip, 0);
+
+ snd_azf3328_gameport_axis_circuit_enable(chip, 0);
+
+ gameport_register_port(chip->gameport);
+
+ return 0;
+}
+
+static void
+snd_azf3328_gameport_free(struct snd_azf3328 *chip)
+{
+ if (chip->gameport) {
+ gameport_unregister_port(chip->gameport);
+ chip->gameport = NULL;
+ }
+ snd_azf3328_gameport_irq_enable(chip, 0);
+}
+#else
+static inline int
+snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
+static inline void
+snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
+static inline void
+snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
+{
+ printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
+}
+#endif /* SUPPORT_GAMEPORT */
+
+/******************************************************************/
+
+static inline void
+snd_azf3328_irq_log_unknown_type(u8 which)
+{
+ snd_azf3328_dbgplay(
+ "azt3328: unknown IRQ type (%x) occurred, please report!\n",
+ which
+ );
+}
+
static irqreturn_t
snd_azf3328_interrupt(int irq, void *dev_id)
{
struct snd_azf3328 *chip = dev_id;
u8 status, which;
+#if DEBUG_PLAY_REC
static unsigned long irq_count;
+#endif
status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS);
/* fast path out, to ease interrupt sharing */
- if (!(status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_MPU401|IRQ_TIMER)))
+ if (!(status &
+ (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
+ ))
return IRQ_NONE; /* must be interrupt for another device */
- snd_azf3328_dbgplay("Interrupt %ld!\nIDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
- irq_count,
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
- status);
-
+ snd_azf3328_dbgplay(
+ "irq_count %ld! IDX_IO_PLAY_FLAGS %04x, "
+ "IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
+ irq_count++ /* debug-only */,
+ snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
+ snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
+ status
+ );
+
if (status & IRQ_TIMER) {
- /* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */
+ /* snd_azf3328_dbgplay("timer %ld\n",
+ snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
+ & TIMER_VALUE_MASK
+ ); */
if (chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
/* ACK timer */
@@ -1232,15 +1637,20 @@ snd_azf3328_interrupt(int irq, void *dev_id)
snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
- if (chip->pcm && chip->playback_substream) {
- snd_pcm_period_elapsed(chip->playback_substream);
+ if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) {
+ snd_pcm_period_elapsed(
+ chip->audio_stream[AZF_PLAYBACK].substream
+ );
snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
which,
- inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
+ snd_azf3328_codec_inl(
+ chip, IDX_IO_PLAY_DMA_CURRPOS
+ )
+ );
} else
- snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+ printk(KERN_WARNING "azt3328: irq handler problem!\n");
if (which & IRQ_PLAY_SOMETHING)
- snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n");
+ snd_azf3328_irq_log_unknown_type(which);
}
if (status & IRQ_RECORDING) {
spin_lock(&chip->reg_lock);
@@ -1249,16 +1659,23 @@ snd_azf3328_interrupt(int irq, void *dev_id)
snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
- if (chip->pcm && chip->capture_substream) {
- snd_pcm_period_elapsed(chip->capture_substream);
+ if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) {
+ snd_pcm_period_elapsed(
+ chip->audio_stream[AZF_CAPTURE].substream
+ );
snd_azf3328_dbgplay("REC period done (#%x), @ %x\n",
which,
- inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
+ snd_azf3328_codec_inl(
+ chip, IDX_IO_REC_DMA_CURRPOS
+ )
+ );
} else
- snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+ printk(KERN_WARNING "azt3328: irq handler problem!\n");
if (which & IRQ_REC_SOMETHING)
- snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n");
+ snd_azf3328_irq_log_unknown_type(which);
}
+ if (status & IRQ_GAMEPORT)
+ snd_azf3328_gameport_interrupt(chip);
/* MPU401 has less critical IRQ requirements
* than timer and playback/recording, right? */
if (status & IRQ_MPU401) {
@@ -1268,7 +1685,6 @@ snd_azf3328_interrupt(int irq, void *dev_id)
* If so, then I don't know how... */
snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
}
- irq_count++;
return IRQ_HANDLED;
}
@@ -1287,8 +1703,8 @@ static const struct snd_pcm_hardware snd_azf3328_playback =
.rates = SNDRV_PCM_RATE_5512 |
SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT,
- .rate_min = 4000,
- .rate_max = 66200,
+ .rate_min = AZF_FREQ_4000,
+ .rate_max = AZF_FREQ_66200,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 65536,
@@ -1315,8 +1731,8 @@ static const struct snd_pcm_hardware snd_azf3328_capture =
.rates = SNDRV_PCM_RATE_5512 |
SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT,
- .rate_min = 4000,
- .rate_max = 66200,
+ .rate_min = AZF_FREQ_4000,
+ .rate_max = AZF_FREQ_66200,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 65536,
@@ -1329,10 +1745,24 @@ static const struct snd_pcm_hardware snd_azf3328_capture =
static unsigned int snd_azf3328_fixed_rates[] = {
- 4000, 4800, 5512, 6620, 8000, 9600, 11025, 13240, 16000, 22050, 32000,
- 44100, 48000, 66200 };
+ AZF_FREQ_4000,
+ AZF_FREQ_4800,
+ AZF_FREQ_5512,
+ AZF_FREQ_6620,
+ AZF_FREQ_8000,
+ AZF_FREQ_9600,
+ AZF_FREQ_11025,
+ AZF_FREQ_13240,
+ AZF_FREQ_16000,
+ AZF_FREQ_22050,
+ AZF_FREQ_32000,
+ AZF_FREQ_44100,
+ AZF_FREQ_48000,
+ AZF_FREQ_66200
+};
+
static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
- .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
+ .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
.list = snd_azf3328_fixed_rates,
.mask = 0,
};
@@ -1346,7 +1776,7 @@ snd_azf3328_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_azf3328_dbgcallenter();
- chip->playback_substream = substream;
+ chip->audio_stream[AZF_PLAYBACK].substream = substream;
runtime->hw = snd_azf3328_playback;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
@@ -1361,7 +1791,7 @@ snd_azf3328_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_azf3328_dbgcallenter();
- chip->capture_substream = substream;
+ chip->audio_stream[AZF_CAPTURE].substream = substream;
runtime->hw = snd_azf3328_capture;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
@@ -1375,7 +1805,7 @@ snd_azf3328_playback_close(struct snd_pcm_substream *substream)
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
snd_azf3328_dbgcallenter();
- chip->playback_substream = NULL;
+ chip->audio_stream[AZF_PLAYBACK].substream = NULL;
snd_azf3328_dbgcallleave();
return 0;
}
@@ -1386,7 +1816,7 @@ snd_azf3328_capture_close(struct snd_pcm_substream *substream)
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
snd_azf3328_dbgcallenter();
- chip->capture_substream = NULL;
+ chip->audio_stream[AZF_CAPTURE].substream = NULL;
snd_azf3328_dbgcallleave();
return 0;
}
@@ -1441,102 +1871,8 @@ snd_azf3328_pcm(struct snd_azf3328 *chip, int device)
/******************************************************************/
-#ifdef SUPPORT_JOYSTICK
-static int __devinit
-snd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev)
-{
- struct gameport *gp;
- struct resource *r;
-
- if (!joystick[dev])
- return -ENODEV;
-
- if (!(r = request_region(0x200, 8, "AZF3328 gameport"))) {
- printk(KERN_WARNING "azt3328: cannot reserve joystick ports\n");
- return -EBUSY;
- }
-
- chip->gameport = gp = gameport_allocate_port();
- if (!gp) {
- printk(KERN_ERR "azt3328: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
- return -ENOMEM;
- }
-
- gameport_set_name(gp, "AZF3328 Gameport");
- gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
- gameport_set_dev_parent(gp, &chip->pci->dev);
- gp->io = 0x200;
- gameport_set_port_data(gp, r);
-
- snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
- snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) | LEGACY_JOY);
-
- gameport_register_port(chip->gameport);
-
- return 0;
-}
-
-static void
-snd_azf3328_free_joystick(struct snd_azf3328 *chip)
-{
- if (chip->gameport) {
- struct resource *r = gameport_get_port_data(chip->gameport);
-
- gameport_unregister_port(chip->gameport);
- chip->gameport = NULL;
- /* disable gameport */
- snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
- snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
- release_and_free_resource(r);
- }
-}
-#else
-static inline int
-snd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
-static inline void
-snd_azf3328_free_joystick(struct snd_azf3328 *chip) { }
-#endif
-
-/******************************************************************/
-
-static int
-snd_azf3328_free(struct snd_azf3328 *chip)
-{
- if (chip->irq < 0)
- goto __end_hw;
-
- /* reset (close) mixer */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); /* first mute master volume */
- snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
-
- /* interrupt setup - mask everything (FIXME!) */
- /* well, at least we know how to disable the timer IRQ */
- snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00);
-
- if (chip->irq >= 0)
- synchronize_irq(chip->irq);
-__end_hw:
- snd_azf3328_free_joystick(chip);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
-}
-
-static int
-snd_azf3328_dev_free(struct snd_device *device)
-{
- struct snd_azf3328 *chip = device->device_data;
- return snd_azf3328_free(chip);
-}
-
-/******************************************************************/
-
-/*** NOTE: the physical timer resolution actually is 1024000 ticks per second,
+/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
+ *** (probably derived from main crystal via a divider of 24),
*** but announcing those attributes to user-space would make programs
*** configure the timer to a 1 tick value, resulting in an absolutely fatal
*** timer IRQ storm.
@@ -1564,7 +1900,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
delay = 49; /* minimum time is 49 ticks */
}
snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
- delay |= TIMER_ENABLE_COUNTDOWN | TIMER_ENABLE_IRQ;
+ delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay);
spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -1582,7 +1918,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
chip = snd_timer_chip(timer);
spin_lock_irqsave(&chip->reg_lock, flags);
/* disable timer countdown and interrupt */
- /* FIXME: should we write TIMER_ACK_IRQ here? */
+ /* FIXME: should we write TIMER_IRQ_ACK here? */
snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_azf3328_dbgcallleave();
@@ -1626,9 +1962,10 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
- if ((err = snd_timer_new(chip->card, "AZF3328", &tid, &timer)) < 0) {
+
+ err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
+ if (err < 0)
goto out;
- }
strcpy(timer->name, "AZF3328 timer");
timer->private_data = chip;
@@ -1636,6 +1973,8 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
chip->timer = timer;
+ snd_azf3328_timer_stop(timer);
+
err = 0;
out:
@@ -1645,10 +1984,44 @@ out:
/******************************************************************/
+static int
+snd_azf3328_free(struct snd_azf3328 *chip)
+{
+ if (chip->irq < 0)
+ goto __end_hw;
+
+ /* reset (close) mixer:
+ * first mute master volume, then reset
+ */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
+ snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+
+ snd_azf3328_timer_stop(chip->timer);
+ snd_azf3328_gameport_free(chip);
+
+ if (chip->irq >= 0)
+ synchronize_irq(chip->irq);
+__end_hw:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+
+ kfree(chip);
+ return 0;
+}
+
+static int
+snd_azf3328_dev_free(struct snd_device *device)
+{
+ struct snd_azf3328 *chip = device->device_data;
+ return snd_azf3328_free(chip);
+}
+
#if 0
/* check whether a bit can be modified */
static void
-snd_azf3328_test_bit(unsigned int reg, int bit)
+snd_azf3328_test_bit(unsigned unsigned reg, int bit)
{
unsigned char val, valoff, valon;
@@ -1659,42 +2032,74 @@ snd_azf3328_test_bit(unsigned int reg, int bit)
outb(val|(1 << bit), reg);
valon = inb(reg);
-
+
outb(val, reg);
- printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n", reg, bit, val, valoff, valon);
+ printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n",
+ reg, bit, val, valoff, valon
+ );
}
#endif
-#if DEBUG_MISC
-static void
+static inline void
snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
{
+#if DEBUG_MISC
u16 tmp;
- snd_azf3328_dbgmisc("codec_port 0x%lx, io2_port 0x%lx, mpu_port 0x%lx, synth_port 0x%lx, mixer_port 0x%lx, irq %d\n", chip->codec_port, chip->io2_port, chip->mpu_port, chip->synth_port, chip->mixer_port, chip->irq);
-
- snd_azf3328_dbgmisc("io2 %02x %02x %02x %02x %02x %02x\n", snd_azf3328_io2_inb(chip, 0), snd_azf3328_io2_inb(chip, 1), snd_azf3328_io2_inb(chip, 2), snd_azf3328_io2_inb(chip, 3), snd_azf3328_io2_inb(chip, 4), snd_azf3328_io2_inb(chip, 5));
-
- for (tmp=0; tmp <= 0x01; tmp += 1)
- snd_azf3328_dbgmisc("0x%02x: opl 0x%04x, mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, mpu330 0x%04x\n", tmp, inb(0x388 + tmp), inb(0x300 + tmp), inb(0x310 + tmp), inb(0x320 + tmp), inb(0x330 + tmp));
+ snd_azf3328_dbgmisc(
+ "codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
+ "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
+ chip->codec_io, chip->game_io, chip->mpu_io,
+ chip->opl3_io, chip->mixer_io, chip->irq
+ );
+
+ snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
+ snd_azf3328_game_inb(chip, 0),
+ snd_azf3328_game_inb(chip, 1),
+ snd_azf3328_game_inb(chip, 2),
+ snd_azf3328_game_inb(chip, 3),
+ snd_azf3328_game_inb(chip, 4),
+ snd_azf3328_game_inb(chip, 5)
+ );
+
+ for (tmp = 0; tmp < 0x07; tmp += 1)
+ snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
+
+ for (tmp = 0; tmp <= 0x07; tmp += 1)
+ snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
+ tmp, inb(0x200 + tmp), inb(0x208 + tmp));
+
+ for (tmp = 0; tmp <= 0x01; tmp += 1)
+ snd_azf3328_dbgmisc(
+ "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
+ "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
+ tmp,
+ inb(0x300 + tmp),
+ inb(0x310 + tmp),
+ inb(0x320 + tmp),
+ inb(0x330 + tmp),
+ inb(0x388 + tmp),
+ inb(0x38c + tmp)
+ );
for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2)
- snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n", tmp, snd_azf3328_codec_inw(chip, tmp));
+ snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_codec_inw(chip, tmp)
+ );
for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
- snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n", tmp, snd_azf3328_mixer_inw(chip, tmp));
+ snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_mixer_inw(chip, tmp)
+ );
+#endif /* DEBUG_MISC */
}
-#else
-static inline void
-snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) {}
-#endif
static int __devinit
snd_azf3328_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct snd_azf3328 ** rchip)
+ struct pci_dev *pci,
+ unsigned long device_type,
+ struct snd_azf3328 **rchip)
{
struct snd_azf3328 *chip;
int err;
@@ -1705,7 +2110,8 @@ snd_azf3328_create(struct snd_card *card,
*rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
@@ -1721,20 +2127,25 @@ snd_azf3328_create(struct snd_card *card,
/* check if we can restrict PCI DMA transfers to 24 bits */
if (pci_set_dma_mask(pci, DMA_24BIT_MASK) < 0 ||
pci_set_consistent_dma_mask(pci, DMA_24BIT_MASK) < 0) {
- snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
+ snd_printk(KERN_ERR "architecture does not support "
+ "24bit PCI busmaster DMA\n"
+ );
err = -ENXIO;
goto out_err;
}
- if ((err = pci_request_regions(pci, "Aztech AZF3328")) < 0) {
+ err = pci_request_regions(pci, "Aztech AZF3328");
+ if (err < 0)
goto out_err;
- }
- chip->codec_port = pci_resource_start(pci, 0);
- chip->io2_port = pci_resource_start(pci, 1);
- chip->mpu_port = pci_resource_start(pci, 2);
- chip->synth_port = pci_resource_start(pci, 3);
- chip->mixer_port = pci_resource_start(pci, 4);
+ chip->codec_io = pci_resource_start(pci, 0);
+ chip->game_io = pci_resource_start(pci, 1);
+ chip->mpu_io = pci_resource_start(pci, 2);
+ chip->opl3_io = pci_resource_start(pci, 3);
+ chip->mixer_io = pci_resource_start(pci, 4);
+
+ chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00;
+ chip->audio_stream[AZF_CAPTURE].portbase = chip->codec_io + 0x20;
if (request_irq(pci->irq, snd_azf3328_interrupt,
IRQF_SHARED, card->shortname, chip)) {
@@ -1747,29 +2158,29 @@ snd_azf3328_create(struct snd_card *card,
synchronize_irq(chip->irq);
snd_azf3328_debug_show_ports(chip);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0)
goto out_err;
- }
/* create mixer interface & switches */
- if ((err = snd_azf3328_mixer_new(chip)) < 0)
+ err = snd_azf3328_mixer_new(chip);
+ if (err < 0)
goto out_err;
-#if 0
- /* set very low bitrate to reduce noise and power consumption? */
- snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, 5512, 8, 1);
-#endif
+ /* shutdown codecs to save power */
+ /* have snd_azf3328_codec_activity() act properly */
+ chip->audio_stream[AZF_PLAYBACK].running = 1;
+ snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
/* standard chip init stuff */
- /* default IRQ init value */
+ /* default IRQ init value */
tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
spin_lock_irq(&chip->reg_lock);
snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp);
snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp);
snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp);
- snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00); /* disable timer */
spin_unlock_irq(&chip->reg_lock);
snd_card_set_dev(card, &pci->dev);
@@ -1805,52 +2216,61 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0 );
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
return -ENOMEM;
strcpy(card->driver, "AZF3328");
strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
- if ((err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+ err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
+ if (err < 0)
goto out_err;
- }
card->private_data = chip;
- if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
- chip->mpu_port, MPU401_INFO_INTEGRATED,
- pci->irq, 0, &chip->rmidi)) < 0) {
- snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
+ err = snd_mpu401_uart_new(
+ card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED,
+ pci->irq, 0, &chip->rmidi
+ );
+ if (err < 0) {
+ snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
+ chip->mpu_io
+ );
goto out_err;
}
- if ((err = snd_azf3328_timer(chip, 0)) < 0) {
+ err = snd_azf3328_timer(chip, 0);
+ if (err < 0)
goto out_err;
- }
- if ((err = snd_azf3328_pcm(chip, 0)) < 0) {
+ err = snd_azf3328_pcm(chip, 0);
+ if (err < 0)
goto out_err;
- }
- if (snd_opl3_create(card, chip->synth_port, chip->synth_port+2,
+ if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
OPL3_HW_AUTO, 1, &opl3) < 0) {
snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
- chip->synth_port, chip->synth_port+2 );
+ chip->opl3_io, chip->opl3_io+2
+ );
} else {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
+ err = snd_opl3_timer_new(opl3, 1, 2);
+ if (err < 0)
+ goto out_err;
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
goto out_err;
- }
}
opl3->private_data = chip;
sprintf(card->longname, "%s at 0x%lx, irq %i",
- card->shortname, chip->codec_port, chip->irq);
+ card->shortname, chip->codec_io, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0)
goto out_err;
- }
#ifdef MODULE
printk(
@@ -1861,19 +2281,18 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
1024000 / seqtimer_scaling, seqtimer_scaling);
#endif
- if (snd_azf3328_config_joystick(chip, dev) < 0)
- snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
- snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
+ snd_azf3328_gameport(chip, dev);
pci_set_drvdata(pci, card);
dev++;
err = 0;
goto out;
-
+
out_err:
+ snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
snd_card_free(card);
-
+
out:
snd_azf3328_dbgcallleave();
return err;
@@ -1894,27 +2313,31 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
- int reg;
+ unsigned reg;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-
+
snd_pcm_suspend_all(chip->pcm);
- for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
- chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
+ chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
/* make sure to disable master volume etc. to prevent looping sound */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
-
- for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
- chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
- chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
- chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
- chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2);
+
+ for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
+ chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2);
+
+ /* manually store the one currently relevant write-only reg, too */
+ chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH;
+
+ for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
+ chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
+ chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
+ chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
pci_disable_device(pci);
pci_save_state(pci);
@@ -1927,7 +2350,7 @@ snd_azf3328_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
- int reg;
+ unsigned reg;
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
@@ -1939,23 +2362,21 @@ snd_azf3328_resume(struct pci_dev *pci)
}
pci_set_master(pci);
- for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
- outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
- outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
- outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
- outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
- outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
+ outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
+ outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
+ outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
+ outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
+ outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
-
-
+#endif /* CONFIG_PM */
static struct pci_driver driver = {
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h
index 679fa992e2b..974e05122f0 100644
--- a/sound/pci/azt3328.h
+++ b/sound/pci/azt3328.h
@@ -1,7 +1,8 @@
#ifndef __SOUND_AZT3328_H
#define __SOUND_AZT3328_H
-/* "PU" == "power-up value", as tested on PCI168 PCI rev. 10 */
+/* "PU" == "power-up value", as tested on PCI168 PCI rev. 10
+ * "WRITE_ONLY" == register does not indicate actual bit values */
/*** main I/O area port indices ***/
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
@@ -54,7 +55,10 @@
#define SOUNDFORMAT_XTAL1 0x00
#define SOUNDFORMAT_XTAL2 0x01
/* all _SUSPECTED_ values are not used by Windows drivers, so we don't
- * have any hard facts, only rough measurements */
+ * have any hard facts, only rough measurements.
+ * All we know is that the crystal used on the board has 24.576MHz,
+ * like many soundcards (which results in the frequencies below when
+ * using certain divider values selected by the values below) */
#define SOUNDFORMAT_FREQ_SUSPECTED_4000 0x0c | SOUNDFORMAT_XTAL1
#define SOUNDFORMAT_FREQ_SUSPECTED_4800 0x0a | SOUNDFORMAT_XTAL1
#define SOUNDFORMAT_FREQ_5510 0x0c | SOUNDFORMAT_XTAL2
@@ -72,6 +76,26 @@
#define SOUNDFORMAT_FLAG_16BIT 0x0010
#define SOUNDFORMAT_FLAG_2CHANNELS 0x0020
+/* define frequency helpers, for maximum value safety */
+enum azf_freq_t {
+#define AZF_FREQ(rate) AZF_FREQ_##rate = rate
+ AZF_FREQ(4000),
+ AZF_FREQ(4800),
+ AZF_FREQ(5512),
+ AZF_FREQ(6620),
+ AZF_FREQ(8000),
+ AZF_FREQ(9600),
+ AZF_FREQ(11025),
+ AZF_FREQ(13240),
+ AZF_FREQ(16000),
+ AZF_FREQ(22050),
+ AZF_FREQ(32000),
+ AZF_FREQ(44100),
+ AZF_FREQ(48000),
+ AZF_FREQ(66200),
+#undef AZF_FREQ
+};
+
/** recording area (see also: playback bit flag definitions) **/
#define IDX_IO_REC_FLAGS 0x20 /* ??, PU:0x0000 */
#define IDX_IO_REC_IRQTYPE 0x22 /* ??, PU:0x0000 */
@@ -97,40 +121,171 @@
/** DirectX timer, main interrupt area (FIXME: and something else?) **/
#define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */
- #define TIMER_VALUE_MASK 0x000fffffUL /* timer countdown value; triggers IRQ when timer is finished */
- #define TIMER_ENABLE_COUNTDOWN 0x01000000UL /* activate the timer countdown */
- #define TIMER_ENABLE_IRQ 0x02000000UL /* trigger timer IRQ on zero transition */
- #define TIMER_ACK_IRQ 0x04000000UL /* being set in IRQ handler in case port 0x00 (hmm, not port 0x64!?!?) had 0x0020 set upon IRQ handler */
+ /* timer countdown value; triggers IRQ when timer is finished */
+ #define TIMER_VALUE_MASK 0x000fffffUL
+ /* activate timer countdown */
+ #define TIMER_COUNTDOWN_ENABLE 0x01000000UL
+ /* trigger timer IRQ on zero transition */
+ #define TIMER_IRQ_ENABLE 0x02000000UL
+ /* being set in IRQ handler in case port 0x00 (hmm, not port 0x64!?!?)
+ * had 0x0020 set upon IRQ handler */
+ #define TIMER_IRQ_ACK 0x04000000UL
#define IDX_IO_IRQSTATUS 0x64
- #define IRQ_PLAYBACK 0x0001
- #define IRQ_RECORDING 0x0002
- #define IRQ_MPU401 0x0010
- #define IRQ_TIMER 0x0020 /* DirectX timer */
- #define IRQ_UNKNOWN1 0x0040 /* probably unused, or possibly I2S port? or gameport IRQ? */
- #define IRQ_UNKNOWN2 0x0080 /* probably unused, or possibly I2S port? or gameport IRQ? */
+ /* some IRQ bit in here might also be used to signal a power-management timer
+ * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
+ * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
+ * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
+
+ #define IRQ_PLAYBACK 0x0001
+ #define IRQ_RECORDING 0x0002
+ #define IRQ_UNKNOWN1 0x0004 /* most probably I2S port */
+ #define IRQ_GAMEPORT 0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
+ #define IRQ_MPU401 0x0010
+ #define IRQ_TIMER 0x0020 /* DirectX timer */
+ #define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly I2S port? */
+ #define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly I2S port? */
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
-#define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */
-#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */
- #define IO_6A_PAUSE_PLAYBACK 0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */
-#define IDX_IO_6CH 0x6C
-#define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */
-/* further I/O indices not saved/restored, so probably not used */
+ /* this is set to e.g. 0x3ff or 0x300, and writable;
+ * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
+#define IDX_IO_SOME_VALUE 0x68
+ #define IO_68_RANDOM_TOGGLE1 0x0100 /* toggles randomly */
+ #define IO_68_RANDOM_TOGGLE2 0x0200 /* toggles randomly */
+ /* umm, nope, behaviour of these bits changes depending on what we wrote
+ * to 0x6b!!
+ * And they change upon playback/stop, too:
+ * Writing a value to 0x68 will display this exact value during playback,
+ * too but when stopped it can fall back to a rather different
+ * seemingly random value). Hmm, possibly this is a register which
+ * has a remote shadow which needs proper device supply which only exists
+ * in case playback is active? Or is this driver-induced?
+ */
+
+/* this WORD can be set to have bits 0x0028 activated (FIXME: correct??);
+ * actually inhibits PCM playback!!! maybe power management??: */
+#define IDX_IO_6AH 0x6A /* WRITE_ONLY! */
+ /* bit 5: enabling this will activate permanent counting of bytes 2/3
+ * at gameport I/O (0xb402/3) (equal values each) and cause
+ * gameport legacy I/O at 0x0200 to be _DISABLED_!
+ * Is this Digital Enhanced Game Port Enable??? Or maybe it's Testmode
+ * for Enhanced Digital Gameport (see 4D Wave DX card): */
+ #define IO_6A_SOMETHING1_GAMEPORT 0x0020
+ /* bit 8; sure, this _pauses_ playback (later resumes at same spot!),
+ * but what the heck is this really about??: */
+ #define IO_6A_PAUSE_PLAYBACK_BIT8 0x0100
+ /* bit 9; sure, this _pauses_ playback (later resumes at same spot!),
+ * but what the heck is this really about??: */
+ #define IO_6A_PAUSE_PLAYBACK_BIT9 0x0200
+ /* BIT8 and BIT9 are _NOT_ able to affect OPL3 MIDI playback,
+ * thus it suggests influence on PCM only!!
+ * However OTOH there seems to be no bit anywhere around here
+ * which is able to disable OPL3... */
+ /* bit 10: enabling this actually changes values at legacy gameport
+ * I/O address (0x200); is this enabling of the Digital Enhanced Game Port???
+ * Or maybe this simply switches off the NE558 circuit, since enabling this
+ * still lets us evaluate button states, but not axis states */
+ #define IO_6A_SOMETHING2_GAMEPORT 0x0400
+ /* writing 0x0300: causes quite some crackling during
+ * PC activity such as switching windows (PCI traffic??
+ * --> FIFO/timing settings???) */
+ /* writing 0x0100 plus/or 0x0200 inhibits playback */
+ /* since the Windows .INF file has Flag_Enable_JoyStick and
+ * Flag_Enable_SB_DOS_Emulation directly together, it stands to reason
+ * that some other bit in this same register might be responsible
+ * for SB DOS Emulation activation (note that the file did NOT define
+ * a switch for OPL3!) */
+#define IDX_IO_6CH 0x6C /* unknown; fully read-writable */
+#define IDX_IO_6EH 0x6E
+ /* writing 0xffff returns 0x83fe (or 0x03fe only).
+ * writing 0x83 (and only 0x83!!) to 0x6f will cause 0x6c to switch
+ * from 0000 to ffff. */
+/* further I/O indices not saved/restored and not readable after writing,
+ * so probably not used */
-/*** I/O 2 area port indices ***/
+
+/*** Gameport area port indices ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
-#define AZF_IO_SIZE_IO2 0x08
-#define AZF_IO_SIZE_IO2_PM 0x06
+#define AZF_IO_SIZE_GAME 0x08
+#define AZF_IO_SIZE_GAME_PM 0x06
+
+enum {
+ AZF_GAME_LEGACY_IO_PORT = 0x200
+};
+
+#define IDX_GAME_LEGACY_COMPATIBLE 0x00
+ /* in some operation mode, writing anything to this port
+ * triggers an interrupt:
+ * yup, that's in case IDX_GAME_01H has one of the
+ * axis measurement bits enabled
+ * (and of course one needs to have GAME_HWCFG_IRQ_ENABLE, too) */
+
+#define IDX_GAME_AXES_CONFIG 0x01
+ /* NOTE: layout of this register awfully similar (read: "identical??")
+ * to AD1815JS.pdf (p.29) */
+
+ /* enables axis 1 (X axis) measurement: */
+ #define GAME_AXES_ENABLE_1 0x01
+ /* enables axis 2 (Y axis) measurement: */
+ #define GAME_AXES_ENABLE_2 0x02
+ /* enables axis 3 (X axis) measurement: */
+ #define GAME_AXES_ENABLE_3 0x04
+ /* enables axis 4 (Y axis) measurement: */
+ #define GAME_AXES_ENABLE_4 0x08
+ /* selects the current axis to read the measured value of
+ * (at IDX_GAME_AXIS_VALUE):
+ * 00 = axis 1, 01 = axis 2, 10 = axis 3, 11 = axis 4: */
+ #define GAME_AXES_READ_MASK 0x30
+ /* enable to have the latch continuously accept ADC values
+ * (and continuously cause interrupts in case interrupts are enabled);
+ * AD1815JS.pdf says it's ~16ms interval there: */
+ #define GAME_AXES_LATCH_ENABLE 0x40
+ /* joystick data (measured axes) ready for reading: */
+ #define GAME_AXES_SAMPLING_READY 0x80
+
+ /* NOTE: other card specs (SiS960 and others!) state that the
+ * game position latches should be frozen when reading and be freed
+ * (== reset?) after reading!!!
+ * Freezing most likely means disabling 0x40 (GAME_AXES_LATCH_ENABLE),
+ * but how to free the value? */
+ /* An internet search for "gameport latch ADC" should provide some insight
+ * into how to program such a gameport system. */
+
+ /* writing 0xf0 to 01H once reset both counters to 0, in some special mode!?
+ * yup, in case 6AH 0x20 is not enabled
+ * (and 0x40 is sufficient, 0xf0 is not needed) */
+
+#define IDX_GAME_AXIS_VALUE 0x02
+ /* R: value of currently configured axis (word value!);
+ * W: trigger axis measurement */
+
+#define IDX_GAME_HWCONFIG 0x04
+ /* note: bits 4 to 7 are never set (== 0) when reading!
+ * --> reserved bits? */
+ /* enables IRQ notification upon axes measurement ready: */
+ #define GAME_HWCFG_IRQ_ENABLE 0x01
+ /* these bits choose a different frequency for the
+ * internal ADC counter increment.
+ * hmm, seems to be a combo of bits:
+ * 00 --> standard frequency
+ * 10 --> 1/2
+ * 01 --> 1/20
+ * 11 --> 1/200: */
+ #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK 0x06
-#define IDX_IO2_LEGACY_ADDR 0x04
- #define LEGACY_SOMETHING 0x01 /* OPL3?? */
- #define LEGACY_JOY 0x08
+ /* enable gameport legacy I/O address (0x200)
+ * I was unable to locate any configurability for a different address: */
+ #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08
+/*** MPU401 ***/
#define AZF_IO_SIZE_MPU 0x04
#define AZF_IO_SIZE_MPU_PM 0x04
-#define AZF_IO_SIZE_SYNTH 0x08
-#define AZF_IO_SIZE_SYNTH_PM 0x06
+/*** OPL3 synth ***/
+#define AZF_IO_SIZE_OPL3 0x08
+#define AZF_IO_SIZE_OPL3_PM 0x06
+/* hmm, given that a standard OPL3 has 4 registers only,
+ * there might be some enhanced functionality lurking at the end
+ * (especially since register 0x04 has a "non-empty" value 0xfe) */
/*** mixer I/O area port indices ***/
/* (only 0x22 of 0x40 bytes saved/restored by Windows driver)
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 4ecdd635ed1..3aa8d973540 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -227,7 +227,6 @@ static inline void snd_bt87x_writel(struct snd_bt87x *chip, u32 reg, u32 value)
static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substream *substream,
unsigned int periods, unsigned int period_bytes)
{
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
unsigned int i, offset;
u32 *risc;
@@ -246,6 +245,7 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea
rest = period_bytes;
do {
u32 cmd, len;
+ unsigned int addr;
len = PAGE_SIZE - (offset % PAGE_SIZE);
if (len > rest)
@@ -260,7 +260,8 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea
if (len == rest)
cmd |= RISC_EOL | RISC_IRQ;
*risc++ = cpu_to_le32(cmd);
- *risc++ = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, offset));
+ addr = snd_pcm_sgbuf_get_addr(substream, offset);
+ *risc++ = cpu_to_le32(addr);
offset += len;
rest -= len;
} while (rest > 0);
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index ecbe79b67e4..a7d89662acf 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -249,6 +249,12 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
.name = "MSI K8N Diamond MB [SB0438]",
.gpio_type = 2,
.i2c_adc = 1 } ,
+ /* MSI K8N Diamond PLUS MB */
+ { .serial = 0x10091102,
+ .name = "MSI K8N Diamond MB",
+ .gpio_type = 2,
+ .i2c_adc = 1,
+ .spi_dac = 2 } ,
/* Shuttle XPC SD31P which has an onboard Creative Labs
* Sound Blaster Live! 24-bit EAX
* high-definition 7.1 audio processor".
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index 893ee4f1ea7..c7885117da3 100644
--- a/sound/pci/ca0106/ca_midi.c
+++ b/sound/pci/ca0106/ca_midi.c
@@ -125,7 +125,8 @@ static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
struct snd_ca_midi *midi = substream->rmidi->private_data;
unsigned long flags;
- snd_assert(midi->dev_id, return -ENXIO);
+ if (snd_BUG_ON(!midi->dev_id))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->midi_mode |= CA_MIDI_MODE_INPUT;
midi->substream_input = substream;
@@ -144,7 +145,8 @@ static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
struct snd_ca_midi *midi = substream->rmidi->private_data;
unsigned long flags;
- snd_assert(midi->dev_id, return -ENXIO);
+ if (snd_BUG_ON(!midi->dev_id))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
midi->substream_output = substream;
@@ -163,7 +165,8 @@ static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
struct snd_ca_midi *midi = substream->rmidi->private_data;
unsigned long flags;
- snd_assert(midi->dev_id, return -ENXIO);
+ if (snd_BUG_ON(!midi->dev_id))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->interrupt_disable(midi,midi->rx_enable);
midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
@@ -181,7 +184,9 @@ static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
unsigned long flags;
- snd_assert(midi->dev_id, return -ENXIO);
+
+ if (snd_BUG_ON(!midi->dev_id))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
@@ -201,7 +206,9 @@ static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- snd_assert(midi->dev_id, return);
+
+ if (snd_BUG_ON(!midi->dev_id))
+ return;
if (up) {
midi->interrupt_enable(midi,midi->rx_enable);
@@ -215,7 +222,8 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int
struct snd_ca_midi *midi = substream->rmidi->private_data;
unsigned long flags;
- snd_assert(midi->dev_id, return);
+ if (snd_BUG_ON(!midi->dev_id))
+ return;
if (up) {
int max = 4;
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 9971b5b7735..1a74ca62c31 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2357,7 +2357,8 @@ static int snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
{
struct cmipci_switch_args *args;
args = (struct cmipci_switch_args *)kcontrol->private_value;
- snd_assert(args != NULL, return -EINVAL);
+ if (snd_BUG_ON(!args))
+ return -EINVAL;
return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);
}
@@ -2401,7 +2402,8 @@ static int snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
{
struct cmipci_switch_args *args;
args = (struct cmipci_switch_args *)kcontrol->private_value;
- snd_assert(args != NULL, return -EINVAL);
+ if (snd_BUG_ON(!args))
+ return -EINVAL;
return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args);
}
@@ -2662,7 +2664,8 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
unsigned int idx;
int err;
- snd_assert(cm != NULL && cm->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!cm || !cm->card))
+ return -EINVAL;
card = cm->card;
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 7556fd90d0e..ef9308f7c45 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -766,13 +766,13 @@ static void snd_cs4281_mode(struct cs4281 *chip, struct cs4281_dma *dma,
if (!capture) {
if (dma->left_slot == chip->src_left_play_slot) {
unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
- snd_assert(dma->right_slot == chip->src_right_play_slot, );
+ snd_BUG_ON(dma->right_slot != chip->src_right_play_slot);
snd_cs4281_pokeBA0(chip, BA0_DACSR, val);
}
} else {
if (dma->left_slot == chip->src_left_rec_slot) {
unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
- snd_assert(dma->right_slot == chip->src_right_rec_slot, );
+ snd_BUG_ON(dma->right_slot != chip->src_right_rec_slot);
snd_cs4281_pokeBA0(chip, BA0_ADCSR, val);
}
}
@@ -1209,7 +1209,8 @@ static void snd_cs4281_gameport_trigger(struct gameport *gameport)
{
struct cs4281 *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return);
+ if (snd_BUG_ON(!chip))
+ return;
snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff);
}
@@ -1217,7 +1218,8 @@ static unsigned char snd_cs4281_gameport_read(struct gameport *gameport)
{
struct cs4281 *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
return snd_cs4281_peekBA0(chip, BA0_JSPT);
}
@@ -1228,7 +1230,8 @@ static int snd_cs4281_gameport_cooked_read(struct gameport *gameport,
struct cs4281 *chip = gameport_get_port_data(gameport);
unsigned js1, js2, jst;
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
js1 = snd_cs4281_peekBA0(chip, BA0_JSC1);
js2 = snd_cs4281_peekBA0(chip, BA0_JSC2);
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index e214e567dec..fb6dc398025 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -90,9 +90,10 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
int count;
unsigned short result,tmp;
u32 offset = 0;
- snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
- (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
- return -EINVAL);
+
+ if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+ codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+ return -EINVAL;
chip->active_ctrl(chip, 1);
@@ -212,9 +213,9 @@ static unsigned short snd_cs46xx_ac97_read(struct snd_ac97 * ac97,
unsigned short val;
int codec_index = ac97->num;
- snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
- codec_index == CS46XX_SECONDARY_CODEC_INDEX,
- return 0xffff);
+ if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+ codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+ return 0xffff;
val = snd_cs46xx_codec_read(chip, reg, codec_index);
@@ -229,9 +230,9 @@ static void snd_cs46xx_codec_write(struct snd_cs46xx *chip,
{
int count;
- snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
- (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
- return);
+ if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+ codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+ return;
chip->active_ctrl(chip, 1);
@@ -294,9 +295,9 @@ static void snd_cs46xx_ac97_write(struct snd_ac97 *ac97,
struct snd_cs46xx *chip = ac97->private_data;
int codec_index = ac97->num;
- snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
- codec_index == CS46XX_SECONDARY_CODEC_INDEX,
- return);
+ if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+ codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+ return;
snd_cs46xx_codec_write(chip, reg, val, codec_index);
}
@@ -315,7 +316,8 @@ int snd_cs46xx_download(struct snd_cs46xx *chip,
unsigned int bank = offset >> 16;
offset = offset & 0xffff;
- snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+ if (snd_BUG_ON((offset & 3) || (len & 3)))
+ return -EINVAL;
dst = chip->region.idx[bank+1].remap_addr + offset;
len /= sizeof(u32);
@@ -343,7 +345,8 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
unsigned int bank = offset >> 16;
offset = offset & 0xffff;
- snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+ if (snd_BUG_ON((offset & 3) || (len & 3)))
+ return -EINVAL;
dst = chip->region.idx[bank+1].remap_addr + offset;
len /= sizeof(u32);
@@ -722,7 +725,9 @@ static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_subst
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
size_t ptr;
struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
- snd_assert (cpcm->pcm_channel,return -ENXIO);
+
+ if (snd_BUG_ON(!cpcm->pcm_channel))
+ return -ENXIO;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
@@ -740,7 +745,8 @@ static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(struct snd_pcm_sub
struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- snd_assert (cpcm->pcm_channel,return -ENXIO);
+ if (snd_BUG_ON(!cpcm->pcm_channel))
+ return -ENXIO;
ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
#else
ptr = snd_cs46xx_peek(chip, BA1_PBA);
@@ -908,7 +914,8 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
cpcm = runtime->private_data;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- snd_assert (sample_rate != 0, return -ENXIO);
+ if (snd_BUG_ON(!sample_rate))
+ return -ENXIO;
mutex_lock(&chip->spos_mutex);
@@ -917,7 +924,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
return -ENXIO;
}
- snd_assert (cpcm->pcm_channel != NULL);
+ snd_BUG_ON(!cpcm->pcm_channel);
if (!cpcm->pcm_channel) {
mutex_unlock(&chip->spos_mutex);
return -ENXIO;
@@ -952,7 +959,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
substream->ops = &snd_cs46xx_playback_iec958_ops;
} else {
- snd_assert(0);
+ snd_BUG();
}
#else
substream->ops = &snd_cs46xx_playback_ops;
@@ -981,7 +988,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;
} else {
- snd_assert(0);
+ snd_BUG();
}
#else
substream->ops = &snd_cs46xx_playback_indirect_ops;
@@ -1029,7 +1036,8 @@ static int snd_cs46xx_playback_prepare(struct snd_pcm_substream *substream)
cpcm = runtime->private_data;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- snd_assert (cpcm->pcm_channel != NULL, return -ENXIO);
+ if (snd_BUG_ON(!cpcm->pcm_channel))
+ return -ENXIO;
pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
pfie &= ~0x0000f03f;
@@ -1714,9 +1722,9 @@ static void snd_cs46xx_mixer_free_ac97(struct snd_ac97 *ac97)
{
struct snd_cs46xx *chip = ac97->private_data;
- snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) ||
- (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]),
- return);
+ if (snd_BUG_ON(ac97 != chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] &&
+ ac97 != chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]))
+ return;
if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) {
chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL;
@@ -1864,7 +1872,7 @@ static int snd_cs46xx_iec958_put(struct snd_kcontrol *kcontrol,
break;
default:
res = -EINVAL;
- snd_assert(0, (void)0);
+ snd_BUG(); /* should never happen ... */
}
return res;
@@ -2236,7 +2244,7 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3);
snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);
} else {
- snd_assert(0); /* should never happen ... */
+ snd_BUG(); /* should never happen ... */
}
udelay(50);
@@ -2553,7 +2561,8 @@ static void snd_cs46xx_gameport_trigger(struct gameport *gameport)
{
struct snd_cs46xx *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return);
+ if (snd_BUG_ON(!chip))
+ return;
snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF);
}
@@ -2561,7 +2570,8 @@ static unsigned char snd_cs46xx_gameport_read(struct gameport *gameport)
{
struct snd_cs46xx *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);
}
@@ -2570,7 +2580,8 @@ static int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes,
struct snd_cs46xx *chip = gameport_get_port_data(gameport);
unsigned js1, js2, jst;
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1);
js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2);
@@ -2754,7 +2765,8 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
{
int idx;
- snd_assert(chip != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
if (chip->active_ctrl)
chip->active_ctrl(chip, 1);
@@ -3489,8 +3501,9 @@ static struct cs_card_type __devinitdata cards[] = {
.name = "Mitac MI6020/21",
.amp = amp_voyetra,
},
+ /* Hercules Game Theatre XP */
{
- .vendor = 0x14AF,
+ .vendor = 0x14af, /* Guillemot Corporation */
.id = 0x0050,
.name = "Hercules Game Theatre XP",
.amp = amp_hercules,
@@ -3532,9 +3545,25 @@ static struct cs_card_type __devinitdata cards[] = {
.amp = amp_hercules,
.mixer_init = hercules_mixer_init,
},
+ /* Herculess Fortissimo */
+ {
+ .vendor = 0x1681,
+ .id = 0xa010,
+ .name = "Hercules Gamesurround Fortissimo II",
+ },
+ {
+ .vendor = 0x1681,
+ .id = 0xa011,
+ .name = "Hercules Gamesurround Fortissimo III 7.1",
+ },
/* Teratec */
{
.vendor = 0x153b,
+ .id = 0x112e,
+ .name = "Terratec DMX XFire 1024",
+ },
+ {
+ .vendor = 0x153b,
.id = 0x1136,
.name = "Terratec SiXPack 5.1",
},
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index ccc8bedb5b1..f4f0c8f5dad 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -63,7 +63,8 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
u32 mop_operands,mop_type,wide_op;
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert( ((size % 2) == 0), return -EINVAL);
+ if (snd_BUG_ON(size %2))
+ return -EINVAL;
while (i < size) {
loval = data[i++];
@@ -289,7 +290,8 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
int i;
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert(ins != NULL, return);
+ if (snd_BUG_ON(!ins))
+ return;
mutex_lock(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) {
@@ -404,7 +406,8 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
/* if module has a code segment it must have
symbol table */
- snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM);
+ if (snd_BUG_ON(!module->symbol_table.symbols))
+ return -ENOMEM;
if (add_symbols(chip,module)) {
snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n");
return -ENOMEM;
@@ -1369,7 +1372,8 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
- snd_assert (chip->nr_ac97_codecs == 1 || chip->nr_ac97_codecs == 2);
+ if (snd_BUG_ON(chip->nr_ac97_codecs != 1 && chip->nr_ac97_codecs != 2))
+ goto _fail_end;
if (chip->nr_ac97_codecs == 1) {
/* output on slot 5 and 11
@@ -1609,11 +1613,14 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST);
- snd_assert(spdifo_scb_desc, return -EIO);
+ if (snd_BUG_ON(!spdifo_scb_desc))
+ return -EIO;
spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST);
- snd_assert(spdifi_scb_desc, return -EIO);
+ if (snd_BUG_ON(!spdifi_scb_desc))
+ return -EIO;
async_codec_scb_desc = cs46xx_dsp_create_scb(chip,"AsynCodecInputSCB",(u32 *)&async_codec_input_scb, HFG_TREE_SCB);
- snd_assert(async_codec_scb_desc, return -EIO);
+ if (snd_BUG_ON(!async_codec_scb_desc))
+ return -EIO;
async_codec_scb_desc->parent_scb_ptr = NULL;
async_codec_scb_desc->next_scb_ptr = spdifi_scb_desc;
@@ -1698,8 +1705,10 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
chip->active_ctrl(chip, 1);
chip->amplifier_ctrl(chip, 1);
- snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
- snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+ if (snd_BUG_ON(ins->asynch_rx_scb))
+ return -EINVAL;
+ if (snd_BUG_ON(!ins->spdif_in_src))
+ return -EINVAL;
mutex_lock(&chip->spos_mutex);
@@ -1754,8 +1763,10 @@ int cs46xx_dsp_disable_spdif_in (struct snd_cs46xx *chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
- snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+ if (snd_BUG_ON(!ins->asynch_rx_scb))
+ return -EINVAL;
+ if (snd_BUG_ON(!ins->spdif_in_src))
+ return -EINVAL;
mutex_lock(&chip->spos_mutex);
@@ -1780,8 +1791,10 @@ int cs46xx_dsp_enable_pcm_capture (struct snd_cs46xx *chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert (ins->pcm_input == NULL,return -EINVAL);
- snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
+ if (snd_BUG_ON(ins->pcm_input))
+ return -EINVAL;
+ if (snd_BUG_ON(!ins->ref_snoop_scb))
+ return -EINVAL;
mutex_lock(&chip->spos_mutex);
ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
@@ -1795,7 +1808,8 @@ int cs46xx_dsp_disable_pcm_capture (struct snd_cs46xx *chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert (ins->pcm_input != NULL,return -EINVAL);
+ if (snd_BUG_ON(!ins->pcm_input))
+ return -EINVAL;
mutex_lock(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->pcm_input);
@@ -1809,8 +1823,10 @@ int cs46xx_dsp_enable_adc_capture (struct snd_cs46xx *chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert (ins->adc_input == NULL,return -EINVAL);
- snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
+ if (snd_BUG_ON(ins->adc_input))
+ return -EINVAL;
+ if (snd_BUG_ON(!ins->codec_in_scb))
+ return -EINVAL;
mutex_lock(&chip->spos_mutex);
ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
@@ -1824,7 +1840,8 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert (ins->adc_input != NULL,return -EINVAL);
+ if (snd_BUG_ON(!ins->adc_input))
+ return -EINVAL;
mutex_lock(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->adc_input);
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 2873cfe48c3..dd7c41b037b 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -46,8 +46,11 @@ static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * s
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int symbol_index = (int)(symbol - ins->symbol_table.symbols);
- snd_assert(ins->symbol_table.nsymbols > 0,return);
- snd_assert(symbol_index >= 0 && symbol_index < ins->symbol_table.nsymbols, return);
+ if (snd_BUG_ON(ins->symbol_table.nsymbols <= 0))
+ return;
+ if (snd_BUG_ON(symbol_index < 0 ||
+ symbol_index >= ins->symbol_table.nsymbols))
+ return;
ins->symbol_table.symbols[symbol_index].deleted = 1;
@@ -116,8 +119,9 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor
if ( scb->parent_scb_ptr ) {
/* unlink parent SCB */
- snd_assert ((scb->parent_scb_ptr->sub_list_ptr == scb ||
- scb->parent_scb_ptr->next_scb_ptr == scb),return);
+ if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr != scb &&
+ scb->parent_scb_ptr->next_scb_ptr != scb))
+ return;
if (scb->parent_scb_ptr->sub_list_ptr == scb) {
@@ -140,7 +144,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor
scb->next_scb_ptr = ins->the_null_scb;
}
} else {
- /* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */
scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;
if (scb->next_scb_ptr != ins->the_null_scb) {
@@ -181,16 +184,17 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
unsigned long flags;
/* check integrety */
- snd_assert ( (scb->index >= 0 &&
- scb->index < ins->nscb &&
- (ins->scbs + scb->index) == scb), return );
+ if (snd_BUG_ON(scb->index < 0 ||
+ scb->index >= ins->nscb ||
+ (ins->scbs + scb->index) != scb))
+ return;
#if 0
/* can't remove a SCB with childs before
removing childs first */
- snd_assert ( (scb->sub_list_ptr == ins->the_null_scb &&
- scb->next_scb_ptr == ins->the_null_scb),
- goto _end);
+ if (snd_BUG_ON(scb->sub_list_ptr != ins->the_null_scb ||
+ scb->next_scb_ptr != ins->the_null_scb))
+ goto _end;
#endif
spin_lock_irqsave(&scb->lock, flags);
@@ -198,7 +202,8 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
spin_unlock_irqrestore(&scb->lock, flags);
cs46xx_dsp_proc_free_scb_desc(scb);
- snd_assert (scb->scb_symbol != NULL, return );
+ if (snd_BUG_ON(!scb->scb_symbol))
+ return;
remove_symbol (chip,scb->scb_symbol);
ins->scbs[scb->index].deleted = 1;
@@ -234,7 +239,6 @@ void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
snd_info_free_entry(scb->proc_info);
scb->proc_info = NULL;
- snd_assert (scb_info != NULL, return);
kfree (scb_info);
}
}
@@ -291,7 +295,8 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
unsigned long flags;
- snd_assert (ins->the_null_scb != NULL,return NULL);
+ if (snd_BUG_ON(!ins->the_null_scb))
+ return NULL;
/* fill the data that will be wroten to DSP */
scb_data[SCBsubListPtr] =
@@ -321,18 +326,20 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
#endif
/* link to parent SCB */
if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
- snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
- return NULL);
+ if (snd_BUG_ON(scb->parent_scb_ptr->next_scb_ptr !=
+ ins->the_null_scb))
+ return NULL;
scb->parent_scb_ptr->next_scb_ptr = scb;
} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
- snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
- return NULL);
+ if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr !=
+ ins->the_null_scb))
+ return NULL;
scb->parent_scb_ptr->sub_list_ptr = scb;
} else {
- snd_assert (0,return NULL);
+ snd_BUG();
}
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -675,7 +682,7 @@ cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
if (pass_through) {
/* wont work with any other rate than
the native DSP rate */
- snd_assert (rate == 48000);
+ snd_BUG_ON(rate != 48000);
scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
dest,"DMAREADER",parent_scb,
@@ -1142,7 +1149,8 @@ find_next_free_scb (struct snd_cs46xx * chip, struct dsp_scb_descriptor * from)
struct dsp_scb_descriptor * scb = from;
while (scb->next_scb_ptr != ins->the_null_scb) {
- snd_assert (scb->next_scb_ptr != NULL, return NULL);
+ if (snd_BUG_ON(!scb->next_scb_ptr))
+ return NULL;
scb = scb->next_scb_ptr;
}
@@ -1246,10 +1254,11 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
break;
case DSP_PCM_S71_CHANNEL:
/* TODO */
- snd_assert(0);
+ snd_BUG();
break;
case DSP_IEC958_CHANNEL:
- snd_assert (ins->asynch_tx_scb != NULL, return NULL);
+ if (snd_BUG_ON(!ins->asynch_tx_scb))
+ return NULL;
mixer_scb = ins->asynch_tx_scb;
/* if sample rate is set to 48khz we pass
@@ -1262,7 +1271,7 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
}
break;
default:
- snd_assert (0);
+ snd_BUG();
return NULL;
}
/* default sample rate is 44100 */
@@ -1308,7 +1317,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
break;
}
}
- snd_assert (src_index != -1,return NULL);
+ if (snd_BUG_ON(src_index == -1))
+ return NULL;
/* we need to create a new SRC SCB */
if (mixer_scb->sub_list_ptr == ins->the_null_scb) {
@@ -1462,9 +1472,10 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
unsigned long flags;
- snd_assert(pcm_channel->active, return );
- snd_assert(ins->npcm_channels > 0, return );
- snd_assert(pcm_channel->src_scb->ref_count > 0, return );
+ if (snd_BUG_ON(!pcm_channel->active ||
+ ins->npcm_channels <= 0 ||
+ pcm_channel->src_scb->ref_count <= 0))
+ return;
spin_lock_irqsave(&chip->reg_lock, flags);
pcm_channel->unlinked = 1;
@@ -1479,8 +1490,9 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
if (!pcm_channel->src_scb->ref_count) {
cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
- snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot < DSP_MAX_SRC_NR,
- return );
+ if (snd_BUG_ON(pcm_channel->src_slot < 0 ||
+ pcm_channel->src_slot >= DSP_MAX_SRC_NR))
+ return;
ins->src_scb_slots[pcm_channel->src_slot] = 0;
ins->nsrc_scb --;
@@ -1490,11 +1502,11 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
struct dsp_pcm_channel_descriptor * pcm_channel)
{
- struct dsp_spos_instance * ins = chip->dsp_spos_instance;
unsigned long flags;
- snd_assert(pcm_channel->active,return -EIO);
- snd_assert(ins->npcm_channels > 0,return -EIO);
+ if (snd_BUG_ON(!pcm_channel->active ||
+ chip->dsp_spos_instance->npcm_channels <= 0))
+ return -EIO;
spin_lock(&pcm_channel->src_scb->lock);
@@ -1537,7 +1549,7 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
- snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
+ snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);
pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -1564,7 +1576,8 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s
struct dsp_scb_descriptor * pcm_input;
int insert_point;
- snd_assert (ins->record_mixer_scb != NULL,return NULL);
+ if (snd_BUG_ON(!ins->record_mixer_scb))
+ return NULL;
if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {
parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr);
@@ -1583,7 +1596,8 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s
int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
{
- snd_assert (src->parent_scb_ptr != NULL, return -EINVAL );
+ if (snd_BUG_ON(!src->parent_scb_ptr))
+ return -EINVAL;
/* mute SCB */
cs46xx_dsp_scb_set_volume (chip,src,0,0);
@@ -1598,8 +1612,10 @@ int cs46xx_src_link(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * parent_scb;
- snd_assert (src->parent_scb_ptr == NULL, return -EINVAL );
- snd_assert(ins->master_mix_scb !=NULL, return -EINVAL );
+ if (snd_BUG_ON(src->parent_scb_ptr))
+ return -EINVAL;
+ if (snd_BUG_ON(!ins->master_mix_scb))
+ return -EINVAL;
if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
@@ -1635,8 +1651,11 @@ int cs46xx_dsp_enable_spdif_out (struct snd_cs46xx *chip)
return -EBUSY;
}
- snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL);
- snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL);
+ if (snd_BUG_ON(ins->asynch_tx_scb))
+ return -EINVAL;
+ if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr !=
+ ins->the_null_scb))
+ return -EINVAL;
/* reset output snooper sample buffer pointer */
snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2,
@@ -1676,10 +1695,15 @@ int cs46xx_dsp_disable_spdif_out (struct snd_cs46xx *chip)
}
/* check integrety */
- snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
- snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL);
- snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL);
- snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL);
+ if (snd_BUG_ON(!ins->asynch_tx_scb))
+ return -EINVAL;
+ if (snd_BUG_ON(!ins->spdif_pcm_input_scb))
+ return -EINVAL;
+ if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr != ins->asynch_tx_scb))
+ return -EINVAL;
+ if (snd_BUG_ON(ins->asynch_tx_scb->parent_scb_ptr !=
+ ins->master_mix_scb))
+ return -EINVAL;
cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
@@ -1734,7 +1758,8 @@ int cs46xx_iec958_post_close (struct snd_cs46xx *chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ins->asynch_tx_scb))
+ return -EINVAL;
ins->spdif_status_out &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN;
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
index 4159e3bc186..29043301ebb 100644
--- a/sound/pci/echoaudio/darla20_dsp.c
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -34,7 +34,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Darla20\n"));
- snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
index 79938eed7e9..60228731841 100644
--- a/sound/pci/echoaudio/darla24_dsp.c
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -34,7 +34,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Darla24\n"));
- snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -148,8 +149,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
static int set_input_clock(struct echoaudio *chip, u16 clock)
{
- snd_assert(clock == ECHO_CLOCK_INTERNAL ||
- clock == ECHO_CLOCK_ESYNC, return -EINVAL);
+ if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL &&
+ clock != ECHO_CLOCK_ESYNC))
+ return -EINVAL;
chip->input_clock = clock;
return set_sample_rate(chip, chip->sample_rate);
}
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index 48eb7c59911..417e25add82 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -47,7 +47,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
local_irq_enable();
DE_INIT(("init_hw() - Echo3G\n"));
- snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -104,9 +105,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if ((err = init_line_levels(chip)) < 0)
return err;
err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
err = set_phantom_power(chip, 0);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
err = set_professional_spdif(chip, TRUE);
DE_INIT(("init_hw done\n"));
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index e16dc92e82f..8dbc5c4ba42 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -490,7 +490,6 @@ static int init_engine(struct snd_pcm_substream *substream,
{
struct echoaudio *chip;
int err, per, rest, page, edge, offs;
- struct snd_sg_buf *sgbuf;
struct audiopipe *pipe;
chip = snd_pcm_substream_chip(substream);
@@ -503,7 +502,7 @@ static int init_engine(struct snd_pcm_substream *substream,
if (pipe->index >= 0) {
DE_HWP(("hwp_ie free(%d)\n", pipe->index));
err = free_pipes(chip, pipe);
- snd_assert(!err);
+ snd_BUG_ON(err);
chip->substream[pipe->index] = NULL;
}
@@ -531,10 +530,6 @@ static int init_engine(struct snd_pcm_substream *substream,
return err;
}
- sgbuf = snd_pcm_substream_sgbuf(substream);
-
- DE_HWP(("pcm_hw_params table size=%d pages=%d\n",
- sgbuf->size, sgbuf->pages));
sglist_init(chip, pipe);
edge = PAGE_SIZE;
for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
@@ -543,16 +538,15 @@ static int init_engine(struct snd_pcm_substream *substream,
if (offs + rest > params_buffer_bytes(hw_params))
rest = params_buffer_bytes(hw_params) - offs;
while (rest) {
+ dma_addr_t addr;
+ addr = snd_pcm_sgbuf_get_addr(substream, offs);
if (rest <= edge - offs) {
- sglist_add_mapping(chip, pipe,
- snd_sgbuf_get_addr(sgbuf, offs),
- rest);
+ sglist_add_mapping(chip, pipe, addr, rest);
sglist_add_irq(chip, pipe);
offs += rest;
rest = 0;
} else {
- sglist_add_mapping(chip, pipe,
- snd_sgbuf_get_addr(sgbuf, offs),
+ sglist_add_mapping(chip, pipe, addr,
edge - offs);
rest -= edge - offs;
offs = edge;
@@ -690,8 +684,10 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
return -EINVAL;
}
- snd_assert(pipe_index < px_num(chip), return -EINVAL);
- snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL);
+ if (snd_BUG_ON(pipe_index >= px_num(chip)))
+ return -EINVAL;
+ if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index)))
+ return -EINVAL;
set_audio_format(chip, pipe_index, &format);
return 0;
}
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
index 52a93318957..c3736bbd819 100644
--- a/sound/pci/echoaudio/echoaudio_3g.c
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -103,9 +103,11 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)
int err, i, o;
/* All audio channels must be closed before changing the digital mode */
- snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+ if (snd_BUG_ON(chip->pipe_alloc_mask))
+ return -EAGAIN;
- snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+ if (snd_BUG_ON(!(chip->digital_modes & (1 << mode))))
+ return -EINVAL;
previous_mode = chip->digital_mode;
err = dsp_set_digital_mode(chip, mode);
@@ -267,8 +269,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
return 0;
}
- snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
- return -EINVAL);
+ if (snd_BUG_ON(rate >= 50000 &&
+ chip->digital_mode == DIGITAL_MODE_ADAT))
+ return -EINVAL;
clock = 0;
control_reg = le32_to_cpu(chip->comm_page->control_register);
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index e6c10077039..be0e18192de 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -474,7 +474,8 @@ static int load_firmware(struct echoaudio *chip)
const struct firmware *fw;
int box_type, err;
- snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM);
+ if (snd_BUG_ON(!chip->dsp_code_to_load || !chip->comm_page))
+ return -EPERM;
/* See if the ASIC is present and working - only if the DSP is already loaded */
if (chip->dsp_code) {
@@ -512,8 +513,8 @@ static int load_firmware(struct echoaudio *chip)
/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */
static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)
{
- snd_assert(index < num_busses_out(chip) + num_busses_in(chip),
- return -EINVAL);
+ if (snd_BUG_ON(index >= num_busses_out(chip) + num_busses_in(chip)))
+ return -EINVAL;
/* Wait for the handshake (OK even if ASIC is not loaded) */
if (wait_handshake(chip))
@@ -536,7 +537,8 @@ static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)
/* Set the gain for a single physical output channel (dB). */
static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)
{
- snd_assert(channel < num_busses_out(chip), return -EINVAL);
+ if (snd_BUG_ON(channel >= num_busses_out(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
@@ -554,8 +556,9 @@ static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)
static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input,
s8 gain)
{
- snd_assert(output < num_busses_out(chip) &&
- input < num_busses_in(chip), return -EINVAL);
+ if (snd_BUG_ON(output >= num_busses_out(chip) ||
+ input >= num_busses_in(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
@@ -1065,8 +1068,10 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)
int i;
DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
- snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL);
- snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL);
+ if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index)))
+ return -EINVAL;
+ if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED))
+ return -EINVAL;
for (channel_mask = i = 0; i < pipe->interleave; i++)
channel_mask |= 1 << (pipe->index + i);
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
index 3aa37e76eba..afa273330e8 100644
--- a/sound/pci/echoaudio/echoaudio_gml.c
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -112,9 +112,11 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)
return -EIO;
/* All audio channels must be closed before changing the digital mode */
- snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+ if (snd_BUG_ON(chip->pipe_alloc_mask))
+ return -EAGAIN;
- snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+ if (snd_BUG_ON(!(chip->digital_modes & (1 << mode))))
+ return -EINVAL;
previous_mode = chip->digital_mode;
err = dsp_set_digital_mode(chip, mode);
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
index 2757c896084..db6c952e9d7 100644
--- a/sound/pci/echoaudio/gina20_dsp.c
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -38,7 +38,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Gina20\n"));
- snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -177,7 +178,8 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
/* Set input bus gain (one unit is 0.5dB !) */
static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
{
- snd_assert(input < num_busses_in(chip), return -EINVAL);
+ if (snd_BUG_ON(input >= num_busses_in(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
index 144fc567bec..2fef37a2a5b 100644
--- a/sound/pci/echoaudio/gina24_dsp.c
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -43,7 +43,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Gina24\n"));
- snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -84,7 +85,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if ((err = init_line_levels(chip)) < 0)
return err;
err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
err = set_professional_spdif(chip, TRUE);
DE_INIT(("init_hw done\n"));
@@ -163,8 +165,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
{
u32 control_reg, clock;
- snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
- return -EINVAL);
+ if (snd_BUG_ON(rate >= 50000 &&
+ chip->digital_mode == DIGITAL_MODE_ADAT))
+ return -EINVAL;
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
index d6ac7734609..f05e39f7aad 100644
--- a/sound/pci/echoaudio/indigo_dsp.c
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -39,7 +39,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Indigo\n"));
- snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -143,8 +144,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
{
int index;
- snd_assert(pipe < num_pipes_out(chip) &&
- output < num_busses_out(chip), return -EINVAL);
+ if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+ output >= num_busses_out(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
index 500e150b49f..90730a5ecb4 100644
--- a/sound/pci/echoaudio/indigodj_dsp.c
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -39,7 +39,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Indigo DJ\n"));
- snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -143,8 +144,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
{
int index;
- snd_assert(pipe < num_pipes_out(chip) &&
- output < num_busses_out(chip), return -EINVAL);
+ if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+ output >= num_busses_out(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
index f3ad13d06be..a7e09ec2107 100644
--- a/sound/pci/echoaudio/indigoio_dsp.c
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -39,7 +39,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Indigo IO\n"));
- snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -114,8 +115,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
{
int index;
- snd_assert(pipe < num_pipes_out(chip) &&
- output < num_busses_out(chip), return -EINVAL);
+ if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+ output >= num_busses_out(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
index 990c9a60a0a..ede75c6ca0f 100644
--- a/sound/pci/echoaudio/layla20_dsp.c
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -42,7 +42,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Layla20\n"));
- snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -155,7 +156,8 @@ static int load_asic(struct echoaudio *chip)
static int set_sample_rate(struct echoaudio *chip, u32 rate)
{
- snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL);
+ if (snd_BUG_ON(rate < 8000 || rate > 50000))
+ return -EINVAL;
/* Only set the clock for internal mode. Do not return failure,
simply treat it as a non-event. */
@@ -252,7 +254,8 @@ static int set_output_clock(struct echoaudio *chip, u16 clock)
/* Set input bus gain (one unit is 0.5dB !) */
static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
{
- snd_assert(input < num_busses_in(chip), return -EINVAL);
+ if (snd_BUG_ON(input >= num_busses_in(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
index 97e42e11514..d61b5cbccca 100644
--- a/sound/pci/echoaudio/layla24_dsp.c
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -42,7 +42,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Layla24\n"));
- snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -73,7 +74,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
return err;
err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
err = set_professional_spdif(chip, TRUE);
DE_INIT(("init_hw done\n"));
@@ -158,8 +160,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
{
u32 control_reg, clock, base_rate;
- snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
- return -EINVAL);
+ if (snd_BUG_ON(rate >= 50000 &&
+ chip->digital_mode == DIGITAL_MODE_ADAT))
+ return -EINVAL;
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
index 891c7051909..227386602f9 100644
--- a/sound/pci/echoaudio/mia_dsp.c
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -42,7 +42,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Mia\n"));
- snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -161,8 +162,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
static int set_input_clock(struct echoaudio *chip, u16 clock)
{
DE_ACT(("set_input_clock(%d)\n", clock));
- snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF,
- return -EINVAL);
+ if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL &&
+ clock != ECHO_CLOCK_SPDIF))
+ return -EINVAL;
chip->input_clock = clock;
return set_sample_rate(chip, chip->sample_rate);
@@ -176,8 +178,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
{
int index;
- snd_assert(pipe < num_pipes_out(chip) &&
- output < num_busses_out(chip), return -EINVAL);
+ if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+ output >= num_busses_out(chip)))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index 91f5bff66d3..77bf2a83d99 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -59,7 +59,8 @@ static int enable_midi_input(struct echoaudio *chip, char enable)
Returns how many actually written or < 0 on error */
static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
{
- snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL);
+ if (snd_BUG_ON(bytes <= 0 || bytes >= MIDI_OUT_BUFFER_SIZE))
+ return -EINVAL;
if (wait_handshake(chip))
return -EIO;
@@ -119,7 +120,8 @@ static int midi_service_irq(struct echoaudio *chip)
/* The count is at index 0, followed by actual data */
count = le16_to_cpu(chip->comm_page->midi_input[0]);
- snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0);
+ if (snd_BUG_ON(count >= MIDI_IN_BUFFER_SIZE))
+ return 0;
/* Get the MIDI data from the comm page */
i = 1;
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
index c0b4bf0be7d..eaa619bd2a0 100644
--- a/sound/pci/echoaudio/mona_dsp.c
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -43,7 +43,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
DE_INIT(("init_hw() - Mona\n"));
- snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV);
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA))
+ return -ENODEV;
if ((err = init_dsp_comm_page(chip))) {
DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -79,7 +80,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
return err;
err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
err = set_professional_spdif(chip, TRUE);
DE_INIT(("init_hw done\n"));
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 45088ebcce5..0e649dcdbf6 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -145,7 +145,8 @@ terminate_voice(struct snd_emux_voice *vp)
{
struct snd_emu10k1 *hw;
- snd_assert(vp, return);
+ if (snd_BUG_ON(!vp))
+ return;
hw = vp->hw;
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
if (vp->block) {
@@ -325,7 +326,8 @@ start_voice(struct snd_emux_voice *vp)
hw = vp->hw;
ch = vp->ch;
- snd_assert(ch >= 0, return -EINVAL);
+ if (snd_BUG_ON(ch < 0))
+ return -EINVAL;
chan = vp->chan;
emem = (struct snd_emu10k1_memblk *)vp->block;
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 548c9cc81af..2f283ea6ad9 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1528,6 +1528,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0151_chip = 1,
.spk71 = 1,
.spdif_bug = 1,
+ .invert_shared_spdif = 1, /* digital/analog switch swapped */
.adc_1361t = 1, /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index 42bae6f7e9a..e10f027bde0 100644
--- a/sound/pci/emu10k1/emu10k1_patch.c
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -46,8 +46,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
struct snd_emu10k1 *emu;
emu = rec->hw;
- snd_assert(sp != NULL, return -EINVAL);
- snd_assert(hdr != NULL, return -EINVAL);
+ if (snd_BUG_ON(!sp || !hdr))
+ return -EINVAL;
if (sp->v.size == 0) {
snd_printd("emu: rom font for sample %d\n", sp->v.sample);
@@ -104,7 +104,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
size = BLANK_HEAD_SIZE;
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
size *= 2;
- snd_assert(offset + size <= blocksize, return -EINVAL);
+ if (offset + size > blocksize)
+ return -EINVAL;
snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
offset += size;
@@ -112,7 +113,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
size = loopend;
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
size *= 2;
- snd_assert(offset + size <= blocksize, return -EINVAL);
+ if (offset + size > blocksize)
+ return -EINVAL;
if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
snd_emu10k1_synth_free(emu, sp->block);
sp->block = NULL;
@@ -129,12 +131,14 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
int woffset;
unsigned short *wblock = (unsigned short*)block;
woffset = offset / 2;
- snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL);
+ if (offset + loopsize * 2 > blocksize)
+ return -EINVAL;
for (i = 0; i < loopsize; i++)
wblock[woffset + i] = wblock[woffset - i -1];
offset += loopsize * 2;
} else {
- snd_assert(offset + loopsize <= blocksize, return -EINVAL);
+ if (offset + loopsize > blocksize)
+ return -EINVAL;
for (i = 0; i < loopsize; i++)
block[offset + i] = block[offset - i -1];
offset += loopsize;
@@ -154,7 +158,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
/* loopend -> sample end */
size = sp->v.size - loopend;
- snd_assert(size >= 0, return -EINVAL);
+ if (size < 0)
+ return -EINVAL;
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
size *= 2;
if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
@@ -212,8 +217,8 @@ snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
struct snd_emu10k1 *emu;
emu = rec->hw;
- snd_assert(sp != NULL, return -EINVAL);
- snd_assert(hdr != NULL, return -EINVAL);
+ if (snd_BUG_ON(!sp || !hdr))
+ return -EINVAL;
if (sp->block) {
snd_emu10k1_synth_free(emu, sp->block);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 491a4a50f86..5ff4dbb62da 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1319,7 +1319,8 @@ static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
unsigned long flags;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
midi->substream_input = substream;
@@ -1345,7 +1346,8 @@ static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream
unsigned long flags;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
midi->substream_output = substream;
@@ -1372,7 +1374,8 @@ static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream
int err = 0;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
snd_emu10k1x_intr_disable(emu, midi->rx_enable);
midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
@@ -1394,7 +1397,8 @@ static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substrea
int err = 0;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
snd_emu10k1x_intr_disable(emu, midi->tx_enable);
midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
@@ -1413,7 +1417,8 @@ static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substr
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
emu = midi->emu;
- snd_assert(emu, return);
+ if (snd_BUG_ON(!emu))
+ return;
if (up)
snd_emu10k1x_intr_enable(emu, midi->rx_enable);
@@ -1428,7 +1433,8 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
unsigned long flags;
emu = midi->emu;
- snd_assert(emu, return);
+ if (snd_BUG_ON(!emu))
+ return;
if (up) {
int max = 4;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 71dc4c8865b..7dba08f0ab8 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -487,7 +487,8 @@ static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,
u32 op, u32 r, u32 a, u32 x, u32 y)
{
u_int32_t *code;
- snd_assert(*ptr < 512, return);
+ if (snd_BUG_ON(*ptr >= 512))
+ return;
code = (u_int32_t __force *)icode->code + (*ptr) * 2;
set_bit(*ptr, icode->code_valid);
code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
@@ -503,7 +504,8 @@ static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,
u32 op, u32 r, u32 a, u32 x, u32 y)
{
u_int32_t *code;
- snd_assert(*ptr < 1024, return);
+ if (snd_BUG_ON(*ptr >= 1024))
+ return;
code = (u_int32_t __force *)icode->code + (*ptr) * 2;
set_bit(*ptr, icode->code_valid);
code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index fd221209abc..f34bbfb705f 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1578,6 +1578,10 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
else
ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
+ if (emu->card_capabilities->invert_shared_spdif)
+ ucontrol->value.integer.value[0] =
+ !ucontrol->value.integer.value[0];
+
return 0;
}
@@ -1586,15 +1590,18 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
{
unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int reg, val;
+ unsigned int reg, val, sw;
int change = 0;
+ sw = ucontrol->value.integer.value[0];
+ if (emu->card_capabilities->invert_shared_spdif)
+ sw = !sw;
spin_lock_irqsave(&emu->reg_lock, flags);
if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
reg = inl(emu->port + A_IOCFG);
- val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
+ val = sw ? A_IOCFG_GPOUT0 : 0;
change = (reg & A_IOCFG_GPOUT0) != val;
if (change) {
reg &= ~A_IOCFG_GPOUT0;
@@ -1603,7 +1610,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
}
}
reg = inl(emu->port + HCFG);
- val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
+ val = sw ? HCFG_GPOUT0 : 0;
change |= (reg & HCFG_GPOUT0) != val;
if (change) {
reg &= ~HCFG_GPOUT0;
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index c4d76d16661..8578c70c61f 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -157,7 +157,8 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
unsigned long flags;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
midi->substream_input = substream;
@@ -183,7 +184,8 @@ static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
unsigned long flags;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
midi->substream_output = substream;
@@ -210,7 +212,8 @@ static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
int err = 0;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
snd_emu10k1_intr_disable(emu, midi->rx_enable);
midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
@@ -232,7 +235,8 @@ static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream
int err = 0;
emu = midi->emu;
- snd_assert(emu, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
spin_lock_irqsave(&midi->open_lock, flags);
snd_emu10k1_intr_disable(emu, midi->tx_enable);
midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
@@ -251,7 +255,8 @@ static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substre
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
emu = midi->emu;
- snd_assert(emu, return);
+ if (snd_BUG_ON(!emu))
+ return;
if (up)
snd_emu10k1_intr_enable(emu, midi->rx_enable);
@@ -266,7 +271,8 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
unsigned long flags;
emu = midi->emu;
- snd_assert(emu, return);
+ if (snd_BUG_ON(!emu))
+ return;
if (up) {
int max = 4;
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 916c1dbcd53..6a47672f930 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -107,7 +107,8 @@ static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct lis
list_for_each (pos, &emu->mapped_link_head) {
struct snd_emu10k1_memblk *blk = get_emu10k1_memblk(pos, mapped_link);
- snd_assert(blk->mapped_page >= 0, continue);
+ if (blk->mapped_page < 0)
+ continue;
size = blk->mapped_page - page;
if (size == npages) {
*nextp = pos;
@@ -295,15 +296,18 @@ struct snd_util_memblk *
snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
struct snd_util_memhdr *hdr;
struct snd_emu10k1_memblk *blk;
int page, err, idx;
- snd_assert(emu, return NULL);
- snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL);
+ if (snd_BUG_ON(!emu))
+ return NULL;
+ if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+ runtime->dma_bytes >= MAXPAGES * EMUPAGESIZE))
+ return NULL;
hdr = emu->memhdr;
- snd_assert(hdr, return NULL);
+ if (snd_BUG_ON(!hdr))
+ return NULL;
mutex_lock(&hdr->block_mutex);
blk = search_empty(emu, runtime->dma_bytes);
@@ -316,16 +320,9 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
*/
idx = 0;
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
+ unsigned long ofs = idx << PAGE_SHIFT;
dma_addr_t addr;
-#ifdef CONFIG_SND_DEBUG
- if (idx >= sgbuf->pages) {
- printk(KERN_ERR "emu: pages overflow! (%d-%d) for %d\n",
- blk->first_page, blk->last_page, sgbuf->pages);
- mutex_unlock(&hdr->block_mutex);
- return NULL;
- }
-#endif
- addr = sgbuf->table[idx].addr;
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
printk(KERN_ERR "emu: failure page = %d\n", idx);
mutex_unlock(&hdr->block_mutex);
@@ -353,7 +350,8 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
*/
int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
{
- snd_assert(emu && blk, return -EINVAL);
+ if (snd_BUG_ON(!emu || !blk))
+ return -EINVAL;
return snd_emu10k1_synth_free(emu, blk);
}
@@ -437,43 +435,49 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
*last_page_ret = last_page;
}
+/* release allocated pages */
+static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
+ int last_page)
+{
+ int page;
+
+ for (page = first_page; page <= last_page; page++) {
+ free_page((unsigned long)emu->page_ptr_table[page]);
+ emu->page_addr_table[page] = 0;
+ emu->page_ptr_table[page] = NULL;
+ }
+}
+
/*
* allocate kernel pages
*/
static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
{
int page, first_page, last_page;
- struct snd_dma_buffer dmab;
emu10k1_memblk_init(blk);
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
/* allocate kernel pages */
for (page = first_page; page <= last_page; page++) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
- PAGE_SIZE, &dmab) < 0)
- goto __fail;
- if (! is_valid_page(emu, dmab.addr)) {
- snd_dma_free_pages(&dmab);
- goto __fail;
+ /* first try to allocate from <4GB zone */
+ struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
+ __GFP_NOWARN);
+ if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
+ if (p)
+ __free_page(p);
+ /* try to allocate from <16MB zone */
+ p = alloc_page(GFP_ATOMIC | GFP_DMA |
+ __GFP_NORETRY | /* no OOM-killer */
+ __GFP_NOWARN);
+ }
+ if (!p) {
+ __synth_free_pages(emu, first_page, page - 1);
+ return -ENOMEM;
}
- emu->page_addr_table[page] = dmab.addr;
- emu->page_ptr_table[page] = dmab.area;
+ emu->page_addr_table[page] = page_to_phys(p);
+ emu->page_ptr_table[page] = page_address(p);
}
return 0;
-
-__fail:
- /* release allocated pages */
- last_page = page - 1;
- for (page = first_page; page <= last_page; page++) {
- dmab.area = emu->page_ptr_table[page];
- dmab.addr = emu->page_addr_table[page];
- dmab.bytes = PAGE_SIZE;
- snd_dma_free_pages(&dmab);
- emu->page_addr_table[page] = 0;
- emu->page_ptr_table[page] = NULL;
- }
-
- return -ENOMEM;
}
/*
@@ -481,23 +485,10 @@ __fail:
*/
static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
{
- int page, first_page, last_page;
- struct snd_dma_buffer dmab;
+ int first_page, last_page;
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
- dmab.dev.type = SNDRV_DMA_TYPE_DEV;
- dmab.dev.dev = snd_dma_pci_data(emu->pci);
- for (page = first_page; page <= last_page; page++) {
- if (emu->page_ptr_table[page] == NULL)
- continue;
- dmab.area = emu->page_ptr_table[page];
- dmab.addr = emu->page_addr_table[page];
- dmab.bytes = PAGE_SIZE;
- snd_dma_free_pages(&dmab);
- emu->page_addr_table[page] = 0;
- emu->page_ptr_table[page] = NULL;
- }
-
+ __synth_free_pages(emu, first_page, last_page);
return 0;
}
@@ -505,7 +496,8 @@ static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *
static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
{
char *ptr;
- snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL);
+ if (snd_BUG_ON(page < 0 || page >= emu->max_cache_pages))
+ return NULL;
ptr = emu->page_ptr_table[page];
if (! ptr) {
printk(KERN_ERR "emu10k1: access to NULL ptr: page = %d\n", page);
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 958cb2a65a4..d7300a1aa26 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -111,8 +111,10 @@ int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
unsigned long flags;
int result;
- snd_assert(rvoice != NULL, return -EINVAL);
- snd_assert(number, return -EINVAL);
+ if (snd_BUG_ON(!rvoice))
+ return -EINVAL;
+ if (snd_BUG_ON(!number))
+ return -EINVAL;
spin_lock_irqsave(&emu->voice_lock, flags);
for (;;) {
@@ -145,7 +147,8 @@ int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
{
unsigned long flags;
- snd_assert(pvoice != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pvoice))
+ return -EINVAL;
spin_lock_irqsave(&emu->voice_lock, flags);
pvoice->interrupt = NULL;
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index fbf1124f7c7..9bf95367c88 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -522,7 +522,7 @@ static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq)
return r;
cond_resched();
}
- snd_printk(KERN_ERR "wait source ready timeout 0x%lx [0x%x]\n",
+ snd_printk(KERN_ERR "wait src ready timeout 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_SMPRATE), r);
return 0;
}
@@ -1629,6 +1629,7 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = ensoniq;
ac97.private_free = snd_ensoniq_mixer_free_ac97;
+ ac97.pci = ensoniq->pci;
ac97.scaps = AC97_SCAP_AUDIO;
if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0)
return err;
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 84fac1fbf10..4cd9a1faaec 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -860,7 +860,8 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
struct es1938 *chip = snd_pcm_substream_chip(substream);
pos <<= chip->dma1_shift;
count <<= chip->dma1_shift;
- snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+ if (snd_BUG_ON(pos + count > chip->dma1_size))
+ return -EINVAL;
if (pos + count < chip->dma1_size) {
if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
return -EFAULT;
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 1bf298d214b..20ee7599600 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -692,7 +692,8 @@ static void apu_data_set(struct es1968 *chip, u16 data)
/* no spinlock */
static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
{
- snd_assert(channel < NR_APUS, return);
+ if (snd_BUG_ON(channel >= NR_APUS))
+ return;
#ifdef CONFIG_PM
chip->apu_map[channel][reg] = data;
#endif
@@ -711,7 +712,8 @@ static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
{
- snd_assert(channel < NR_APUS, return 0);
+ if (snd_BUG_ON(channel >= NR_APUS))
+ return 0;
reg |= (channel << 4);
apu_index_set(chip, reg);
return __maestro_read(chip, IDR0_DATA_PORT);
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ab0c726d648..1980c6d207e 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -5,6 +5,7 @@ snd-hda-intel-y := hda_intel.o
snd-hda-intel-y += hda_codec.o
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
+snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o
@@ -14,5 +15,6 @@ snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
+snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
new file mode 100644
index 00000000000..9b77b3e0fa9
--- /dev/null
+++ b/sound/pci/hda/hda_beep.c
@@ -0,0 +1,134 @@
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/pci.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include "hda_beep.h"
+
+enum {
+ DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
+ DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */
+ DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
+};
+
+static void snd_hda_generate_beep(struct work_struct *work)
+{
+ struct hda_beep *beep =
+ container_of(work, struct hda_beep, beep_work);
+ struct hda_codec *codec = beep->codec;
+
+ /* generate tone */
+ snd_hda_codec_write_cache(codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, beep->tone);
+}
+
+static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct hda_beep *beep = input_get_drvdata(dev);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 1000;
+ case SND_TONE:
+ hz *= 1000; /* fixed point */
+ hz = hz - DIGBEEP_HZ_MIN;
+ if (hz < 0)
+ hz = 0; /* turn off PC beep*/
+ else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
+ hz = 0xff;
+ else {
+ hz /= DIGBEEP_HZ_STEP;
+ hz++;
+ }
+ break;
+ default:
+ return -1;
+ }
+ beep->tone = hz;
+
+ /* schedule beep event */
+ schedule_work(&beep->beep_work);
+ return 0;
+}
+
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ struct input_dev *input_dev;
+ struct hda_beep *beep;
+ int err;
+
+ beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+ if (beep == NULL)
+ return -ENOMEM;
+ snprintf(beep->phys, sizeof(beep->phys),
+ "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
+ input_dev = input_allocate_device();
+
+ /* setup digital beep device */
+ input_dev->name = "HDA Digital PCBeep";
+ input_dev->phys = beep->phys;
+ input_dev->id.bustype = BUS_PCI;
+
+ input_dev->id.vendor = codec->vendor_id >> 16;
+ input_dev->id.product = codec->vendor_id & 0xffff;
+ input_dev->id.version = 0x01;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ input_dev->event = snd_hda_beep_event;
+ input_dev->dev.parent = &codec->bus->pci->dev;
+ input_set_drvdata(input_dev, beep);
+
+ err = input_register_device(input_dev);
+ if (err < 0) {
+ input_free_device(input_dev);
+ kfree(beep);
+ return err;
+ }
+
+ /* enable linear scale */
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_2, 0x01);
+
+ beep->nid = nid;
+ beep->dev = input_dev;
+ beep->codec = codec;
+ codec->beep = beep;
+
+ INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+ return 0;
+}
+
+void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+ struct hda_beep *beep = codec->beep;
+ if (beep) {
+ cancel_work_sync(&beep->beep_work);
+ flush_scheduled_work();
+
+ input_unregister_device(beep->dev);
+ kfree(beep);
+ }
+}
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
new file mode 100644
index 00000000000..de4036e6e71
--- /dev/null
+++ b/sound/pci/hda/hda_beep.h
@@ -0,0 +1,44 @@
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_HDA_BEEP_H
+#define __SOUND_HDA_BEEP_H
+
+#include "hda_codec.h"
+
+/* beep information */
+struct hda_beep {
+ struct input_dev *dev;
+ struct hda_codec *codec;
+ char phys[32];
+ int tone;
+ int nid;
+ struct work_struct beep_work; /* scheduled task for beep event */
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
+void snd_hda_detach_beep_device(struct hda_codec *codec);
+#else
+#define snd_hda_attach_beep_device(...)
+#define snd_hda_detach_beep_device(...)
+#endif
+#endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a6be6e3e871..6447754ae56 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -94,6 +94,9 @@ static const struct hda_codec_preset *hda_preset_tables[] = {
#ifdef CONFIG_SND_HDA_CODEC_VIA
snd_hda_preset_via,
#endif
+#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
+ snd_hda_preset_nvhdmi,
+#endif
NULL
};
@@ -211,7 +214,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
unsigned int shift, num_elems, mask;
hda_nid_t prev_nid;
- snd_assert(conn_list && max_conns > 0, return -EINVAL);
+ if (snd_BUG_ON(!conn_list || max_conns <= 0))
+ return -EINVAL;
parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
if (parm & AC_CLIST_LONG) {
@@ -313,7 +317,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
}
/*
- * process queueud unsolicited events
+ * process queued unsolicited events
*/
static void process_unsol_events(struct work_struct *work)
{
@@ -407,8 +411,10 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
.dev_free = snd_hda_bus_dev_free,
};
- snd_assert(temp, return -EINVAL);
- snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL);
+ if (snd_BUG_ON(!temp))
+ return -EINVAL;
+ if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response))
+ return -EINVAL;
if (busp)
*busp = NULL;
@@ -585,11 +591,13 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
struct hda_codec **codecp)
{
struct hda_codec *codec;
- char component[13];
+ char component[31];
int err;
- snd_assert(bus, return -EINVAL);
- snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);
+ if (snd_BUG_ON(!bus))
+ return -EINVAL;
+ if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+ return -EINVAL;
if (bus->caddr_tbl[codec_addr]) {
snd_printk(KERN_ERR "hda_codec: "
@@ -688,7 +696,7 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
snd_hda_create_hwdep(codec);
#endif
- sprintf(component, "HDA:%08x", codec->vendor_id);
+ sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
snd_component_add(codec->bus->card, component);
if (codecp)
@@ -956,15 +964,6 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
}
#endif /* SND_HDA_NEEDS_RESUME */
-/*
- * AMP control callbacks
- */
-/* retrieve parameters from private_value */
-#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
-#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
-#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
-
/* volume */
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1430,6 +1429,29 @@ static unsigned int convert_to_spdif_status(unsigned short val)
return sbits;
}
+/* set digital convert verbs both for the given NID and its slaves */
+static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
+ int verb, int val)
+{
+ hda_nid_t *d;
+
+ snd_hda_codec_write(codec, nid, 0, verb, val);
+ d = codec->slave_dig_outs;
+ if (!d)
+ return;
+ for (; *d; d++)
+ snd_hda_codec_write(codec, *d, 0, verb, val);
+}
+
+static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
+ int dig1, int dig2)
+{
+ if (dig1 != -1)
+ set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
+ if (dig2 != -1)
+ set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
+}
+
static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1448,14 +1470,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
change = codec->spdif_ctls != val;
codec->spdif_ctls = val;
- if (change) {
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- val & 0xff);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_2,
- val >> 8);
- }
+ if (change)
+ set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
mutex_unlock(&codec->spdif_mutex);
return change;
@@ -1487,9 +1503,7 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
change = codec->spdif_ctls != val;
if (change) {
codec->spdif_ctls = val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- val & 0xff);
+ set_dig_out_convert(codec, nid, val & 0xff, -1);
/* unmute amp switch (if any) */
if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
(val & AC_DIG1_ENABLE))
@@ -2236,11 +2250,13 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
if (info->ops.close == NULL)
info->ops.close = hda_pcm_default_open_close;
if (info->ops.prepare == NULL) {
- snd_assert(info->nid, return -EINVAL);
+ if (snd_BUG_ON(!info->nid))
+ return -EINVAL;
info->ops.prepare = hda_pcm_default_prepare;
}
if (info->ops.cleanup == NULL) {
- snd_assert(info->nid, return -EINVAL);
+ if (snd_BUG_ON(!info->nid))
+ return -EINVAL;
info->ops.cleanup = hda_pcm_default_cleanup;
}
return 0;
@@ -2335,7 +2351,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
if (!tbl)
return -1;
if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
char tmp[10];
const char *model = NULL;
if (models)
@@ -2583,14 +2599,31 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
unsigned int stream_tag, unsigned int format)
{
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_ctls & AC_DIG1_ENABLE)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ set_dig_out_convert(codec, nid,
+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+ -1);
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ if (codec->slave_dig_outs) {
+ hda_nid_t *d;
+ for (d = codec->slave_dig_outs; *d; d++)
+ snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
+ format);
+ }
/* turn on again (if needed) */
- if (codec->spdif_ctls & AC_DIG1_ENABLE)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ set_dig_out_convert(codec, nid,
+ codec->spdif_ctls & 0xff, -1);
+}
+
+static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+ snd_hda_codec_cleanup_stream(codec, nid);
+ if (codec->slave_dig_outs) {
+ hda_nid_t *d;
+ for (d = codec->slave_dig_outs; *d; d++)
+ snd_hda_codec_cleanup_stream(codec, *d);
+ }
}
/*
@@ -2602,7 +2635,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
mutex_lock(&codec->spdif_mutex);
if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
/* already opened as analog dup; reset it once */
- snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
mout->dig_out_used = HDA_DIG_EXCLUSIVE;
mutex_unlock(&codec->spdif_mutex);
return 0;
@@ -2697,7 +2730,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
stream_tag, format);
} else {
mout->dig_out_used = 0;
- snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
}
}
mutex_unlock(&codec->spdif_mutex);
@@ -2748,7 +2781,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
mout->extra_out_nid[i]);
mutex_lock(&codec->spdif_mutex);
if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
- snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
mout->dig_out_used = 0;
}
mutex_unlock(&codec->spdif_mutex);
@@ -2756,7 +2789,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
}
/*
- * Helper for automatic ping configuration
+ * Helper for automatic pin configuration
*/
static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index dcd390b2bba..60468f56240 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -78,7 +78,7 @@ enum {
#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
-#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e
+#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
/* f10-f1a: GPIO */
#define AC_VERB_GET_GPIO_DATA 0x0f15
@@ -90,6 +90,14 @@ enum {
#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
/* f20: AFG/MFG */
#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
+#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
+#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
+#define AC_VERB_GET_HDMI_ELDD 0x0f2f
+#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
+#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
+#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
+#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
+#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
/*
* SET verbs
@@ -121,7 +129,14 @@ enum {
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
+#define AC_VERB_SET_EAPD 0x788
#define AC_VERB_SET_CODEC_RESET 0x7ff
+#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
+#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
+#define AC_VERB_SET_HDMI_DIP_DATA 0x731
+#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
+#define AC_VERB_SET_HDMI_CP_CTRL 0x733
+#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
/*
* Parameter IDs
@@ -143,6 +158,7 @@ enum {
#define AC_PAR_GPIO_CAP 0x11
#define AC_PAR_AMP_OUT_CAP 0x12
#define AC_PAR_VOL_KNB_CAP 0x13
+#define AC_PAR_HDMI_LPCM_CAP 0x20
/*
* AC_VERB_PARAMETERS results (32bit)
@@ -171,6 +187,8 @@ enum {
#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
#define AC_WCAP_POWER (1<<10) /* power control */
#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
+#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
+#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
#define AC_WCAP_DELAY (0xf<<16)
#define AC_WCAP_DELAY_SHIFT 16
#define AC_WCAP_TYPE (0xf<<20)
@@ -206,9 +224,20 @@ enum {
/* Input converter SDI select */
#define AC_SDI_SELECT (0xf<<0)
-/* Unsolicited response */
+/* Unsolicited response control */
#define AC_UNSOL_TAG (0x3f<<0)
#define AC_UNSOL_ENABLED (1<<7)
+#define AC_USRSP_EN AC_UNSOL_ENABLED
+
+/* Unsolicited responses */
+#define AC_UNSOL_RES_TAG (0x3f<<26)
+#define AC_UNSOL_RES_TAG_SHIFT 26
+#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
+#define AC_UNSOL_RES_SUBTAG_SHIFT 21
+#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
+#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
+#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
+#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
/* Pin widget capabilies */
#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
@@ -222,6 +251,10 @@ enum {
* but is marked reserved in the Intel HDA specification.
*/
#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
+/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
+ * in HD-audio specification
+ */
+#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
#define AC_PINCAP_VREF (0x37<<8)
#define AC_PINCAP_VREF_SHIFT 8
#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
@@ -272,6 +305,22 @@ enum {
#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
#define AC_KNBCAP_DELTA (1<<7)
+/* HDMI LPCM capabilities */
+#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
+#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
+#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
+#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
+#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
+#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
+#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
+#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
+#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
+#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
+#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
+#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
+#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
+#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
+
/*
* Control Parameters
*/
@@ -317,18 +366,44 @@ enum {
#define AC_PINCTL_OUT_EN (1<<6)
#define AC_PINCTL_HP_EN (1<<7)
-/* Unsolicited response - 8bit */
-#define AC_USRSP_EN (1<<7)
-
/* Pin sense - 32bit */
#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
#define AC_PINSENSE_PRESENCE (1<<31)
+#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
/* EAPD/BTL enable - 32bit */
#define AC_EAPDBTL_BALANCED (1<<0)
#define AC_EAPDBTL_EAPD (1<<1)
#define AC_EAPDBTL_LR_SWAP (1<<2)
+/* HDMI ELD data */
+#define AC_ELDD_ELD_VALID (1<<31)
+#define AC_ELDD_ELD_DATA 0xff
+
+/* HDMI DIP size */
+#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
+#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
+
+/* HDMI DIP index */
+#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
+#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
+
+/* HDMI DIP xmit (transmit) control */
+#define AC_DIPXMIT_MASK (0x3<<6)
+#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
+#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
+#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
+
+/* HDMI content protection (CP) control */
+#define AC_CPCTRL_CES (1<<9) /* current encryption state */
+#define AC_CPCTRL_READY (1<<8) /* ready bit */
+#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
+#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
+
+/* Converter channel <-> HDMI slot mapping */
+#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
+#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
+
/* configuration default - 32bit */
#define AC_DEFCFG_SEQUENCE (0xf<<0)
#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
@@ -449,6 +524,7 @@ enum {
*/
struct hda_bus;
+struct hda_beep;
struct hda_codec;
struct hda_pcm;
struct hda_pcm_stream;
@@ -634,6 +710,9 @@ struct hda_codec {
/* codec specific info */
void *spec;
+ /* beep device */
+ struct hda_beep *beep;
+
/* widget capabilities cache */
unsigned int num_nodes;
hda_nid_t start_nid;
@@ -646,9 +725,15 @@ struct hda_codec {
unsigned int spdif_status; /* IEC958 status bits */
unsigned short spdif_ctls; /* SPDIF control bits */
unsigned int spdif_in_enable; /* SPDIF input enable? */
+ hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_hwdep *hwdep; /* assigned hwdep device */
+ /* misc flags */
+ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
+ * status change
+ * (e.g. Realtek codecs)
+ */
#ifdef CONFIG_SND_HDA_POWER_SAVE
unsigned int power_on :1; /* current (global) power-state */
unsigned int power_transition :1; /* power-state in transition */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 59e4389c94a..0ca30894f7c 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -174,7 +174,8 @@ static int build_afg_tree(struct hda_codec *codec)
int i, nodes, err;
hda_nid_t nid;
- snd_assert(spec, return -EINVAL);
+ if (snd_BUG_ON(!spec))
+ return -EINVAL;
spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 2177d9af533..6e18a422d99 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -88,7 +88,7 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
{
-#ifndef CONFIG_SND_DEBUG_DETECT
+#ifndef CONFIG_SND_DEBUG_VERBOSE
if (!capable(CAP_SYS_RAWIO))
return -EACCES;
#endif
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b3a618eb42c..9f316c1b279 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -55,6 +55,7 @@ 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 bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int single_cmd;
static int enable_msi;
@@ -69,7 +70,9 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
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).");
+ "(0 = auto, 1 = none, 2 = POSBUF).");
+module_param_array(bdl_pos_adj, int, NULL, 0644);
+MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param(single_cmd, bool, 0444);
@@ -98,6 +101,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH8},"
"{Intel, ICH9},"
"{Intel, ICH10},"
+ "{Intel, PCH},"
"{Intel, SCH},"
"{ATI, SB450},"
"{ATI, SB600},"
@@ -197,6 +201,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define ATIHDMI_NUM_CAPTURE 0
#define ATIHDMI_NUM_PLAYBACK 1
+/* TERA has 4 playback and 3 capture */
+#define TERA_NUM_CAPTURE 3
+#define TERA_NUM_PLAYBACK 4
+
/* this number is statically defined for simplicity */
#define MAX_AZX_DEV 16
@@ -214,9 +222,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
-/* STATESTS int mask: SD2,SD1,SD0 */
-#define AZX_MAX_CODECS 3
-#define STATESTS_INT_MASK 0x07
+/* STATESTS int mask: S3,SD2,SD1,SD0 */
+#define AZX_MAX_CODECS 4
+#define STATESTS_INT_MASK 0x0f
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
@@ -259,9 +267,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* position fix mode */
enum {
POS_FIX_AUTO,
- POS_FIX_NONE,
+ POS_FIX_LPIB,
POS_FIX_POSBUF,
- POS_FIX_FIFO,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@@ -271,11 +278,19 @@ enum {
/* Defines for Nvidia HDA support */
#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
+#define NVIDIA_HDA_ISTRM_COH 0x4d
+#define NVIDIA_HDA_OSTRM_COH 0x4c
+#define NVIDIA_HDA_ENABLE_COHBIT 0x01
/* Defines for Intel SCH HDA snoop control */
#define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+/* Define IN stream 0 FIFO size offset in VIA controller */
+#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
+/* Define VIA HD Audio Device ID*/
+#define VIA_HDAC_DEVICE_ID 0x3288
+
/*
*/
@@ -285,6 +300,7 @@ struct azx_dev {
u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */
+ unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */
@@ -301,11 +317,17 @@ struct azx_dev {
*/
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
- /* for sanity check of position buffer */
- unsigned int period_intr;
unsigned int opened :1;
unsigned int running :1;
+ unsigned int irq_pending :1;
+ unsigned int irq_ignore :1;
+ /*
+ * For VIA:
+ * A flag to ensure DMA position is 0
+ * when link position is not greater than FIFO size
+ */
+ unsigned int insufficient :1;
};
/* CORB/RIRB */
@@ -323,6 +345,7 @@ struct azx_rb {
struct azx {
struct snd_card *card;
struct pci_dev *pci;
+ int dev_index;
/* chip type specific */
int driver_type;
@@ -366,9 +389,14 @@ struct azx {
unsigned int single_cmd :1;
unsigned int polling_mode :1;
unsigned int msi :1;
+ unsigned int irq_pending_warned :1;
+ unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
/* for debugging */
unsigned int last_cmd; /* last issued command (to sync) */
+
+ /* for pending irqs */
+ struct work_struct irq_pending_work;
};
/* driver types */
@@ -381,6 +409,8 @@ enum {
AZX_DRIVER_SIS,
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
+ AZX_DRIVER_TERA,
+ AZX_NUM_DRIVERS, /* keep this as last entry */
};
static char *driver_short_names[] __devinitdata = {
@@ -392,6 +422,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_SIS] = "HDA SIS966",
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
+ [AZX_DRIVER_TERA] = "HDA Teradici",
};
/*
@@ -426,11 +457,6 @@ static char *driver_short_names[] __devinitdata = {
/* for pcm support */
#define get_azx_dev(substream) (substream->runtime->private_data)
-/* Get the upper 32bit of the given dma_addr_t
- * Compiler should optimize and eliminate the code if dma_addr_t is 32bit
- */
-#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0)
-
static int azx_acquire_irq(struct azx *chip, int do_disconnect);
/*
@@ -461,7 +487,7 @@ static void azx_init_cmd_io(struct azx *chip)
chip->corb.addr = chip->rb.addr;
chip->corb.buf = (u32 *)chip->rb.area;
azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
- azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr));
+ azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
/* set the corb size to 256 entries (ULI requires explicitly) */
azx_writeb(chip, CORBSIZE, 0x02);
@@ -476,7 +502,7 @@ static void azx_init_cmd_io(struct azx *chip)
chip->rirb.addr = chip->rb.addr + 2048;
chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
- azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));
+ azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
/* set the rirb size to 256 entries (ULI requires explicitly) */
azx_writeb(chip, RIRBSIZE, 0x02);
@@ -805,6 +831,11 @@ static void azx_int_clear(struct azx *chip)
/* start a stream */
static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
{
+ /*
+ * Before stream start, initialize parameter
+ */
+ azx_dev->insufficient = 1;
+
/* enable SIE */
azx_writeb(chip, INTCTL,
azx_readb(chip, INTCTL) | (1 << azx_dev->index));
@@ -847,7 +878,7 @@ static void azx_init_chip(struct azx *chip)
/* program the position buffer */
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
- azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+ azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
chip->initialized = 1;
}
@@ -890,6 +921,12 @@ static void azx_init_pci(struct azx *chip)
update_pci_byte(chip->pci,
NVIDIA_HDA_TRANSREG_ADDR,
0x0f, NVIDIA_HDA_ENABLE_COHBITS);
+ update_pci_byte(chip->pci,
+ NVIDIA_HDA_ISTRM_COH,
+ 0x01, NVIDIA_HDA_ENABLE_COHBIT);
+ update_pci_byte(chip->pci,
+ NVIDIA_HDA_OSTRM_COH,
+ 0x01, NVIDIA_HDA_ENABLE_COHBIT);
break;
case AZX_DRIVER_SCH:
pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
@@ -908,6 +945,8 @@ static void azx_init_pci(struct azx *chip)
}
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+
/*
* interrupt handler
*/
@@ -930,11 +969,23 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
azx_dev = &chip->azx_dev[i];
if (status & azx_dev->sd_int_sta_mask) {
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
- if (azx_dev->substream && azx_dev->running) {
- azx_dev->period_intr++;
+ if (!azx_dev->substream || !azx_dev->running)
+ continue;
+ /* ignore the first dummy IRQ (due to pos_adj) */
+ if (azx_dev->irq_ignore) {
+ azx_dev->irq_ignore = 0;
+ continue;
+ }
+ /* check whether this IRQ is really acceptable */
+ if (azx_position_ok(chip, azx_dev)) {
+ azx_dev->irq_pending = 0;
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(azx_dev->substream);
spin_lock(&chip->reg_lock);
+ } else {
+ /* bogus IRQ, process it later */
+ azx_dev->irq_pending = 1;
+ schedule_work(&chip->irq_pending_work);
}
}
}
@@ -959,59 +1010,108 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+ struct azx_dev *azx_dev, u32 **bdlp,
+ int ofs, int size, int with_ioc)
+{
+ u32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ azx_dev->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
* set up BDL entries
*/
-static int azx_setup_periods(struct snd_pcm_substream *substream,
+static int azx_setup_periods(struct azx *chip,
+ struct snd_pcm_substream *substream,
struct azx_dev *azx_dev)
{
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
u32 *bdl;
int i, ofs, periods, period_bytes;
+ int pos_adj;
/* reset BDL address */
azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0);
period_bytes = snd_pcm_lib_period_bytes(substream);
+ azx_dev->period_bytes = period_bytes;
periods = azx_dev->bufsize / period_bytes;
/* program the initial BDL entries */
bdl = (u32 *)azx_dev->bdl.area;
ofs = 0;
azx_dev->frags = 0;
- for (i = 0; i < periods; i++) {
- int size, rest;
- if (i >= AZX_MAX_BDL_ENTRIES) {
- snd_printk(KERN_ERR "Too many BDL entries: "
- "buffer=%d, period=%d\n",
- azx_dev->bufsize, period_bytes);
- /* reset */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
- return -EINVAL;
+ azx_dev->irq_ignore = 0;
+ pos_adj = bdl_pos_adj[chip->dev_index];
+ if (pos_adj > 0) {
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int pos_align = pos_adj;
+ pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
+ if (!pos_adj)
+ pos_adj = pos_align;
+ else
+ pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
+ pos_align;
+ pos_adj = frames_to_bytes(runtime, pos_adj);
+ if (pos_adj >= period_bytes) {
+ snd_printk(KERN_WARNING "Too big adjustment %d\n",
+ bdl_pos_adj[chip->dev_index]);
+ pos_adj = 0;
+ } else {
+ ofs = setup_bdle(substream, azx_dev,
+ &bdl, ofs, pos_adj, 1);
+ if (ofs < 0)
+ goto error;
+ azx_dev->irq_ignore = 1;
}
- rest = period_bytes;
- do {
- dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
- /* program the address field of the BDL entry */
- bdl[0] = cpu_to_le32((u32)addr);
- bdl[1] = cpu_to_le32(upper_32bit(addr));
- /* program the size field of the BDL entry */
- size = PAGE_SIZE - (ofs % PAGE_SIZE);
- if (rest < size)
- size = rest;
- bdl[2] = cpu_to_le32(size);
- /* program the IOC to enable interrupt
- * only when the whole fragment is processed
- */
- rest -= size;
- bdl[3] = rest ? 0 : cpu_to_le32(0x01);
- bdl += 4;
- azx_dev->frags++;
- ofs += size;
- } while (rest > 0);
+ } else
+ pos_adj = 0;
+ for (i = 0; i < periods; i++) {
+ if (i == periods - 1 && pos_adj)
+ ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+ period_bytes - pos_adj, 0);
+ else
+ ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+ period_bytes, 1);
+ if (ofs < 0)
+ goto error;
}
return 0;
+
+ error:
+ snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
+ azx_dev->bufsize, period_bytes);
+ /* reset */
+ azx_sd_writel(azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(azx_dev, SD_BDLPU, 0);
+ return -EINVAL;
}
/*
@@ -1062,11 +1162,12 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
/* lower BDL address */
azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
/* upper BDL address */
- azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr));
+ azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
/* enable the position buffer */
if (chip->position_fix == POS_FIX_POSBUF ||
- chip->position_fix == POS_FIX_AUTO) {
+ chip->position_fix == POS_FIX_AUTO ||
+ chip->via_dmapos_patch) {
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
azx_writel(chip, DPLBASE,
(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@@ -1084,15 +1185,18 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
* Codec initialization
*/
-static unsigned int azx_max_codecs[] __devinitdata = {
+/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
+static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
+ [AZX_DRIVER_TERA] = 1,
+};
+
+/* number of slots to probe as default
+ * this can be different from azx_max_codecs[] -- e.g. some boards
+ * report wrongly the non-existing 4th slot availability
+ */
+static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
[AZX_DRIVER_ICH] = 3,
- [AZX_DRIVER_SCH] = 3,
- [AZX_DRIVER_ATI] = 4,
- [AZX_DRIVER_ATIHDMI] = 4,
- [AZX_DRIVER_VIA] = 3, /* FIXME: correct? */
- [AZX_DRIVER_SIS] = 3, /* FIXME: correct? */
- [AZX_DRIVER_ULI] = 3, /* FIXME: correct? */
- [AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */
+ [AZX_DRIVER_ATI] = 3,
};
static int __devinit azx_codec_create(struct azx *chip, const char *model,
@@ -1100,6 +1204,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
{
struct hda_bus_template bus_temp;
int c, codecs, audio_codecs, err;
+ int def_slots, max_slots;
memset(&bus_temp, 0, sizeof(bus_temp));
bus_temp.private_data = chip;
@@ -1115,8 +1220,17 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
if (err < 0)
return err;
+ if (chip->driver_type == AZX_DRIVER_NVIDIA)
+ chip->bus->needs_damn_long_delay = 1;
+
codecs = audio_codecs = 0;
- for (c = 0; c < AZX_MAX_CODECS; c++) {
+ max_slots = azx_max_codecs[chip->driver_type];
+ if (!max_slots)
+ max_slots = AZX_MAX_CODECS;
+ def_slots = azx_default_codecs[chip->driver_type];
+ if (!def_slots)
+ def_slots = max_slots;
+ for (c = 0; c < def_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, &codec);
@@ -1129,7 +1243,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++) {
+ for (; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
err = snd_hda_codec_new(chip->bus, c, NULL);
if (err < 0)
@@ -1316,7 +1430,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
azx_dev->bufsize, azx_dev->format_val);
- if (azx_setup_periods(substream, azx_dev) < 0)
+ if (azx_setup_periods(chip, substream, azx_dev) < 0)
return -EINVAL;
azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -1421,35 +1535,173 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
+/* get the current DMA position with correction on VIA chips */
+static unsigned int azx_via_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ unsigned int link_pos, mini_pos, bound_pos;
+ unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
+ unsigned int fifo_size;
+
+ link_pos = azx_sd_readl(azx_dev, SD_LPIB);
+ if (azx_dev->index >= 4) {
+ /* Playback, no problem using link position */
+ return link_pos;
+ }
+
+ /* Capture */
+ /* For new chipset,
+ * use mod to get the DMA position just like old chipset
+ */
+ mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
+ mod_dma_pos %= azx_dev->period_bytes;
+
+ /* azx_dev->fifo_size can't get FIFO size of in stream.
+ * Get from base address + offset.
+ */
+ fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
+
+ if (azx_dev->insufficient) {
+ /* Link position never gather than FIFO size */
+ if (link_pos <= fifo_size)
+ return 0;
+
+ azx_dev->insufficient = 0;
+ }
+
+ if (link_pos <= fifo_size)
+ mini_pos = azx_dev->bufsize + link_pos - fifo_size;
+ else
+ mini_pos = link_pos - fifo_size;
+
+ /* Find nearest previous boudary */
+ mod_mini_pos = mini_pos % azx_dev->period_bytes;
+ mod_link_pos = link_pos % azx_dev->period_bytes;
+ if (mod_link_pos >= fifo_size)
+ bound_pos = link_pos - mod_link_pos;
+ else if (mod_dma_pos >= mod_mini_pos)
+ bound_pos = mini_pos - mod_mini_pos;
+ else {
+ bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
+ if (bound_pos >= azx_dev->bufsize)
+ bound_pos = 0;
+ }
+
+ /* Calculate real DMA position we want */
+ return bound_pos + mod_dma_pos;
+}
+
+static unsigned int azx_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ unsigned int pos;
+
+ if (chip->via_dmapos_patch)
+ pos = azx_via_get_position(chip, azx_dev);
+ else if (chip->position_fix == POS_FIX_POSBUF ||
+ chip->position_fix == POS_FIX_AUTO) {
+ /* use the position buffer */
+ pos = le32_to_cpu(*azx_dev->posbuf);
+ } else {
+ /* read LPIB */
+ pos = azx_sd_readl(azx_dev, SD_LPIB);
+ }
+ if (pos >= azx_dev->bufsize)
+ pos = 0;
+ return pos;
+}
+
static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev = get_azx_dev(substream);
+ return bytes_to_frames(substream->runtime,
+ azx_get_position(chip, azx_dev));
+}
+
+/*
+ * Check whether the current DMA position is acceptable for updating
+ * periods. Returns non-zero if it's OK.
+ *
+ * Many HD-audio controllers appear pretty inaccurate about
+ * the update-IRQ timing. The IRQ is issued before actually the
+ * data is processed. So, we need to process it afterwords in a
+ * workqueue.
+ */
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+{
unsigned int pos;
- if (chip->position_fix == POS_FIX_POSBUF ||
- chip->position_fix == POS_FIX_AUTO) {
- /* use the position buffer */
- pos = le32_to_cpu(*azx_dev->posbuf);
- if (chip->position_fix == POS_FIX_AUTO &&
- azx_dev->period_intr == 1 && !pos) {
+ pos = azx_get_position(chip, azx_dev);
+ if (chip->position_fix == POS_FIX_AUTO) {
+ if (!pos) {
printk(KERN_WARNING
"hda-intel: Invalid position buffer, "
"using LPIB read method instead.\n");
- chip->position_fix = POS_FIX_NONE;
- goto read_lpib;
+ chip->position_fix = POS_FIX_LPIB;
+ pos = azx_get_position(chip, azx_dev);
+ } else
+ chip->position_fix = POS_FIX_POSBUF;
+ }
+
+ if (!bdl_pos_adj[chip->dev_index])
+ return 1; /* no delayed ack */
+ if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
+ return 0; /* NG - it's below the period boundary */
+ return 1; /* OK, it's fine */
+}
+
+/*
+ * The work for pending PCM period updates.
+ */
+static void azx_irq_pending_work(struct work_struct *work)
+{
+ struct azx *chip = container_of(work, struct azx, irq_pending_work);
+ int i, pending;
+
+ if (!chip->irq_pending_warned) {
+ printk(KERN_WARNING
+ "hda-intel: IRQ timing workaround is activated "
+ "for card #%d. Suggest a bigger bdl_pos_adj.\n",
+ chip->card->number);
+ chip->irq_pending_warned = 1;
+ }
+
+ for (;;) {
+ pending = 0;
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < chip->num_streams; i++) {
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
+ if (!azx_dev->irq_pending ||
+ !azx_dev->substream ||
+ !azx_dev->running)
+ continue;
+ if (azx_position_ok(chip, azx_dev)) {
+ azx_dev->irq_pending = 0;
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(azx_dev->substream);
+ spin_lock(&chip->reg_lock);
+ } else
+ pending++;
}
- } else {
- read_lpib:
- /* read LPIB */
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- if (chip->position_fix == POS_FIX_FIFO)
- pos += azx_dev->fifo_size;
+ spin_unlock_irq(&chip->reg_lock);
+ if (!pending)
+ return;
+ cond_resched();
}
- if (pos >= azx_dev->bufsize)
- pos = 0;
- return bytes_to_frames(substream->runtime, pos);
+}
+
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
+{
+ int i;
+
+ spin_lock_irq(&chip->reg_lock);
+ for (i = 0; i < chip->num_streams; i++)
+ chip->azx_dev[i].irq_pending = 0;
+ spin_unlock_irq(&chip->reg_lock);
+ flush_scheduled_work();
}
static struct snd_pcm_ops azx_pcm_ops = {
@@ -1482,7 +1734,8 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
return 0;
- snd_assert(cpcm->name, return -EINVAL);
+ if (snd_BUG_ON(!cpcm->name))
+ return -EINVAL;
err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
cpcm->stream[0].substreams,
@@ -1506,7 +1759,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
- 1024 * 64, 1024 * 1024);
+ 1024 * 64, 32 * 1024 * 1024);
chip->pcm[cpcm->device] = pcm;
return 0;
}
@@ -1676,6 +1929,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ azx_clear_irq_pending(chip);
for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]);
if (chip->initialized)
@@ -1732,6 +1986,7 @@ static int azx_free(struct azx *chip)
int i;
if (chip->initialized) {
+ azx_clear_irq_pending(chip);
for (i = 0; i < chip->num_streams; i++)
azx_stream_stop(chip, &chip->azx_dev[i]);
azx_stop_chip(chip);
@@ -1770,9 +2025,9 @@ static int azx_dev_free(struct snd_device *device)
* white/black-listing for position_fix
*/
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
- SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
- SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE),
- SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE),
+ SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
{}
};
@@ -1780,6 +2035,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
{
const struct snd_pci_quirk *q;
+ /* Check VIA HD Audio Controller exist */
+ if (chip->pci->vendor == PCI_VENDOR_ID_VIA &&
+ chip->pci->device == VIA_HDAC_DEVICE_ID) {
+ chip->via_dmapos_patch = 1;
+ /* Use link position directly, avoid any transfer problem. */
+ return POS_FIX_LPIB;
+ }
+ chip->via_dmapos_patch = 0;
+
if (fix == POS_FIX_AUTO) {
q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) {
@@ -1857,12 +2121,25 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->irq = -1;
chip->driver_type = driver_type;
chip->msi = enable_msi;
+ chip->dev_index = dev;
+ INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
chip->position_fix = check_position_fix(chip, position_fix[dev]);
check_probe_mask(chip, dev);
chip->single_cmd = single_cmd;
+ if (bdl_pos_adj[dev] < 0) {
+ switch (chip->driver_type) {
+ case AZX_DRIVER_ICH:
+ bdl_pos_adj[dev] = 1;
+ break;
+ default:
+ bdl_pos_adj[dev] = 32;
+ break;
+ }
+ }
+
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
if (chip->driver_type == AZX_DRIVER_ULI) {
@@ -2089,10 +2366,13 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
+ /* PCH */
+ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
/* ATI SB 450/600 */
@@ -2102,6 +2382,7 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI },
{ PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI },
{ PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0x970f), .driver_data = AZX_DRIVER_ATIHDMI },
{ PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI },
{ PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI },
{ PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI },
@@ -2141,6 +2422,8 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
+ /* Teradici */
+ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5c9e578f7f2..7957fefda73 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -368,12 +368,15 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
#define AMP_OUT_UNMUTE 0xb000
#define AMP_OUT_ZERO 0xb000
/* pinctl values */
-#define PIN_IN 0x20
-#define PIN_VREF80 0x24
-#define PIN_VREF50 0x21
-#define PIN_OUT 0x40
-#define PIN_HP 0xc0
-#define PIN_HP_AMP 0x80
+#define PIN_IN (AC_PINCTL_IN_EN)
+#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
+#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
+#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT (AC_PINCTL_OUT_EN)
+#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
+#define PIN_HP_AMP (AC_PINCTL_HP_EN)
/*
* get widget capabilities
@@ -418,4 +421,13 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
hda_nid_t nid);
#endif /* CONFIG_SND_HDA_POWER_SAVE */
+/*
+ * AMP control callbacks
+ */
+/* retrieve parameters from private_value */
+#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
+#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
+#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
+#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
index 2fdf2358dbc..dfbcfa88da4 100644
--- a/sound/pci/hda/hda_patch.h
+++ b/sound/pci/hda/hda_patch.h
@@ -18,3 +18,5 @@ extern struct hda_codec_preset snd_hda_preset_atihdmi[];
extern struct hda_codec_preset snd_hda_preset_conexant[];
/* VIA codecs */
extern struct hda_codec_preset snd_hda_preset_via[];
+/* NVIDIA HDMI codecs */
+extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 5633f77f8f3..743d77922bc 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -216,7 +216,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
unsigned int caps, val;
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
- snd_iprintf(buffer, " Pincap 0x08%x:", caps);
+ snd_iprintf(buffer, " Pincap 0x%08x:", caps);
if (caps & AC_PINCAP_IN)
snd_iprintf(buffer, " IN");
if (caps & AC_PINCAP_OUT)
@@ -229,8 +229,13 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
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_HDMI) {
+ /* Realtek uses this bit as a different meaning */
+ if ((codec->vendor_id >> 16) == 0x10ec)
+ snd_iprintf(buffer, " R/L");
+ else
+ snd_iprintf(buffer, " HDMI");
+ }
if (caps & AC_PINCAP_TRIG_REQ)
snd_iprintf(buffer, " Trigger");
if (caps & AC_PINCAP_IMP_SENSE)
@@ -366,8 +371,6 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
{
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");
@@ -386,7 +389,8 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
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);
+ snd_iprintf(buffer, " Digital category: 0x%x\n",
+ (digi1 >> 8) & AC_DIG2_CC);
}
static const char *get_pwr_state(u32 state)
@@ -553,9 +557,15 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
get_wid_type_name(wid_type), wid_caps);
- if (wid_caps & AC_WCAP_STEREO)
- snd_iprintf(buffer, " Stereo");
- else
+ if (wid_caps & AC_WCAP_STEREO) {
+ unsigned int chans;
+ chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+ chans = ((chans << 1) | 1) + 1;
+ if (chans == 2)
+ snd_iprintf(buffer, " Stereo");
+ else
+ snd_iprintf(buffer, " %d-Channels", chans);
+ } else
snd_iprintf(buffer, " Mono");
if (wid_caps & AC_WCAP_DIGITAL)
snd_iprintf(buffer, " Digital");
@@ -567,6 +577,8 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, " Stripe");
if (wid_caps & AC_WCAP_LR_SWAP)
snd_iprintf(buffer, " R/L");
+ if (wid_caps & AC_WCAP_CP_CAPS)
+ snd_iprintf(buffer, " CP");
snd_iprintf(buffer, "\n");
/* volume knob is a special widget that always have connection
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index a99e86d7427..2b00c4afdf9 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -23,7 +23,6 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/mutex.h>
#include <sound/core.h>
#include "hda_codec.h"
@@ -64,7 +63,6 @@ struct ad198x_spec {
/* PCM information */
struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
- struct mutex amp_mutex; /* PCM volume/mute control mutex */
unsigned int spdif_route;
/* dynamic controls, init_verbs and input_mux */
@@ -1618,6 +1616,7 @@ static const char *ad1981_models[AD1981_MODELS] = {
static struct snd_pci_quirk ad1981_cfg_tbl[] = {
SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
+ SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
/* All HP models */
SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
@@ -1827,9 +1826,14 @@ static hda_nid_t ad1988_capsrc_nids[3] = {
0x0c, 0x0d, 0x0e
};
-#define AD1988_SPDIF_OUT 0x02
+#define AD1988_SPDIF_OUT 0x02
+#define AD1988_SPDIF_OUT_HDMI 0x0b
#define AD1988_SPDIF_IN 0x07
+static hda_nid_t ad1989b_slave_dig_outs[2] = {
+ AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI
+};
+
static struct hda_input_mux ad1988_6stack_capture_source = {
.num_items = 5,
.items = {
@@ -2144,6 +2148,7 @@ static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
{ } /* end */
};
@@ -2208,6 +2213,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
/* Analog CD Input */
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ /* Analog Mix output amp */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ }
};
@@ -2248,8 +2255,12 @@ static struct hda_verb ad1988_spdif_init_verbs[] = {
/* AD1989 has no ADC -> SPDIF route */
static struct hda_verb ad1989_spdif_init_verbs[] = {
- /* SPDIF out pin */
+ /* SPDIF-1 out pin */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+ /* SPDIF-2/HDMI out pin */
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
{ }
};
@@ -2337,6 +2348,8 @@ static struct hda_verb ad1988_3stack_init_verbs[] = {
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Analog Mix output amp */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ }
};
@@ -2410,6 +2423,8 @@ static struct hda_verb ad1988_laptop_init_verbs[] = {
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Analog Mix output amp */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ }
};
@@ -2623,7 +2638,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
{
struct ad198x_spec *spec = codec->spec;
hda_nid_t nid;
- int idx, err;
+ int i, idx, err;
char name[32];
if (! pin)
@@ -2631,16 +2646,26 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
idx = ad1988_pin_idx(pin);
nid = ad1988_idx_to_dac(codec, idx);
- /* specify the DAC as the extra output */
- if (! spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else
- spec->multiout.extra_out_nid[0] = nid;
- /* control HP volume/switch on the output mixer amp */
- sprintf(name, "%s Playback Volume", pfx);
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
- return err;
+ /* check whether the corresponding DAC was already taken */
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t pin = spec->autocfg.line_out_pins[i];
+ hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
+ if (dac == nid)
+ break;
+ }
+ if (i >= spec->autocfg.line_outs) {
+ /* specify the DAC as the extra output */
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = nid;
+ else
+ spec->multiout.extra_out_nid[0] = nid;
+ /* control HP volume/switch on the output mixer amp */
+ sprintf(name, "%s Playback Volume", pfx);
+ err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
nid = ad1988_mixer_nids[idx];
sprintf(name, "%s Playback Switch", pfx);
if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
@@ -2859,6 +2884,7 @@ static struct snd_pci_quirk ad1988_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
+ SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
{}
};
@@ -2966,6 +2992,7 @@ static int patch_ad1988(struct hda_codec *codec)
ad1989_spdif_out_mixers;
spec->init_verbs[spec->num_init_verbs++] =
ad1989_spdif_init_verbs;
+ codec->slave_dig_outs = ad1989b_slave_dig_outs;
} else {
spec->mixers[spec->num_mixers++] =
ad1988_spdif_out_mixers;
@@ -3177,7 +3204,6 @@ static int patch_ad1884(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
@@ -3847,7 +3873,6 @@ static int patch_ad1884a(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
@@ -3904,7 +3929,7 @@ static int patch_ad1884a(struct hda_codec *codec)
/*
- * AD1882
+ * AD1882 / AD1882A
*
* port-A - front hp-out
* port-B - front mic-in
@@ -3941,6 +3966,18 @@ static struct hda_input_mux ad1882_capture_source = {
},
};
+/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
+static struct hda_input_mux ad1882a_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "Front Mic", 0x1 },
+ { "Mic", 0x4},
+ { "Line", 0x2 },
+ { "Digital Mic", 0x06 },
+ { "Mix", 0x7 },
+ },
+};
+
static struct snd_kcontrol_new ad1882_base_mixers[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
@@ -3950,16 +3987,7 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
HDA_CODEC_MUTE("Front 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("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+
HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
@@ -3992,6 +4020,35 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
{ } /* end */
};
+static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
+ 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("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
+ 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("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+ HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
@@ -4152,7 +4209,6 @@ static int patch_ad1882(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 6;
@@ -4162,9 +4218,16 @@ static int patch_ad1882(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
spec->adc_nids = ad1882_adc_nids;
spec->capsrc_nids = ad1882_capsrc_nids;
- spec->input_mux = &ad1882_capture_source;
- spec->num_mixers = 1;
+ if (codec->vendor_id == 0x11d1882)
+ spec->input_mux = &ad1882_capture_source;
+ else
+ spec->input_mux = &ad1882a_capture_source;
+ spec->num_mixers = 2;
spec->mixers[0] = ad1882_base_mixers;
+ if (codec->vendor_id == 0x11d1882)
+ spec->mixers[1] = ad1882_loopback_mixers;
+ else
+ spec->mixers[1] = ad1882a_loopback_mixers;
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1882_init_verbs;
spec->spdif_route = 0;
@@ -4181,8 +4244,8 @@ static int patch_ad1882(struct hda_codec *codec)
switch (board_config) {
default:
case AD1882_3STACK:
- spec->num_mixers = 2;
- spec->mixers[1] = ad1882_3stack_mixers;
+ spec->num_mixers = 3;
+ spec->mixers[2] = ad1882_3stack_mixers;
spec->channel_mode = ad1882_modes;
spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
spec->need_dac_fix = 1;
@@ -4190,8 +4253,8 @@ static int patch_ad1882(struct hda_codec *codec)
spec->multiout.num_dacs = 1;
break;
case AD1882_6STACK:
- spec->num_mixers = 2;
- spec->mixers[1] = ad1882_6stack_mixers;
+ spec->num_mixers = 3;
+ spec->mixers[2] = ad1882_6stack_mixers;
break;
}
return 0;
@@ -4214,6 +4277,7 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
+ { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
{ .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
{} /* terminator */
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index 12272508b11..ba61575983f 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -35,6 +35,9 @@ struct atihdmi_spec {
struct hda_pcm pcm_rec;
};
+#define CVT_NID 0x02 /* audio converter */
+#define PIN_NID 0x03 /* HDMI output pin */
+
static struct hda_verb atihdmi_basic_init[] = {
/* enable digital output on pin widget */
{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
@@ -60,8 +63,9 @@ static int atihdmi_init(struct hda_codec *codec)
{
snd_hda_sequence_write(codec, atihdmi_basic_init);
/* SI codec requires to unmute the pin */
- if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, PIN_NID, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
return 0;
}
@@ -92,15 +96,29 @@ static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct atihdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
+ int chans = substream->runtime->channels;
+ int i, err;
+
+ err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+ if (err < 0)
+ return err;
+ snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
+ chans - 1);
+ /* FIXME: XXX */
+ for (i = 0; i < chans; i++) {
+ snd_hda_codec_write(codec, CVT_NID, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (i << 4) | i);
+ }
+ return 0;
}
static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- .nid = 0x2, /* NID to query formats and rates and setup streams */
+ .nid = CVT_NID, /* NID to query formats and rates and setup streams */
.ops = {
.open = atihdmi_dig_playback_pcm_open,
.close = atihdmi_dig_playback_pcm_close,
@@ -112,6 +130,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec)
{
struct atihdmi_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm_rec;
+ unsigned int chans;
codec->num_pcms = 1;
codec->pcm_info = info;
@@ -120,6 +139,13 @@ static int atihdmi_build_pcms(struct hda_codec *codec)
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
+ /* FIXME: we must check ELD and change the PCM parameters dynamically
+ */
+ chans = get_wcaps(codec, CVT_NID);
+ chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13;
+ chans = ((chans << 1) | 1) + 1;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+
return 0;
}
@@ -147,9 +173,11 @@ static int patch_atihdmi(struct hda_codec *codec)
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital,
- * seems to be unused in pure-digital
- * case. */
+ /* NID for copying analog to digital,
+ * seems to be unused in pure-digital
+ * case.
+ */
+ spec->multiout.dig_out_nid = CVT_NID;
codec->patch_ops = atihdmi_patch_ops;
@@ -164,6 +192,7 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+ { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
{} /* terminator */
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 36fd8526003..7c1eb23f0ce 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -82,7 +82,6 @@ struct conexant_spec {
/* PCM information */
struct hda_pcm pcm_rec[2]; /* used in build_pcms() */
- struct mutex amp_mutex; /* PCM volume/mute control mutex */
unsigned int spdif_route;
/* dynamic controls, init_verbs and input_mux */
@@ -687,7 +686,7 @@ static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
static struct hda_verb cxt5045_init_verbs[] = {
/* Line in, Mic */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ {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 },
/* HP, Amp */
{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
@@ -907,10 +906,12 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
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(0x1734, 0x110e, "Fujitsu V5505",
+ CXT5045_LAPTOP_HPMICSENSE),
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),
@@ -928,7 +929,6 @@ static int patch_cxt5045(struct hda_codec *codec)
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
@@ -963,6 +963,7 @@ static int patch_cxt5045(struct hda_codec *codec)
codec->patch_ops.init = cxt5045_init;
break;
case CXT5045_LAPTOP_MICSENSE:
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
spec->input_mux = &cxt5045_capture_source;
spec->num_init_verbs = 2;
spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
@@ -1007,15 +1008,19 @@ static int patch_cxt5045(struct hda_codec *codec)
#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));
+ switch (codec->subsystem_id >> 16) {
+ case 0x103c:
+ /* HP laptop has a really bad sound over 0dB on NID 0x17.
+ * Fix max PCM level to 0 dB
+ * (originall it has 0x2b steps with 0dB offset 0x14)
+ */
+ 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));
+ break;
+ }
return 0;
}
@@ -1477,7 +1482,6 @@ static int patch_cxt5047(struct hda_codec *codec)
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
@@ -1736,7 +1740,6 @@ static int patch_cxt5051(struct hda_codec *codec)
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
codec->patch_ops = conexant_patch_ops;
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
new file mode 100644
index 00000000000..1a65775d28e
--- /dev/null
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -0,0 +1,164 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for NVIDIA HDMI codecs
+ *
+ * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
+ * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct nvhdmi_spec {
+ struct hda_multi_out multiout;
+
+ struct hda_pcm pcm_rec;
+};
+
+static struct hda_verb nvhdmi_basic_init[] = {
+ /* enable digital output on pin widget */
+ { 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {} /* terminator */
+};
+
+/*
+ * Controls
+ */
+static int nvhdmi_build_controls(struct hda_codec *codec)
+{
+ struct nvhdmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int nvhdmi_init(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, nvhdmi_basic_init);
+ return 0;
+}
+
+/*
+ * Digital out
+ */
+static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct nvhdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct nvhdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct nvhdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x4, /* NID to query formats and rates and setup streams */
+ .rates = SNDRV_PCM_RATE_48000,
+ .maxbps = 16,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .ops = {
+ .open = nvhdmi_dig_playback_pcm_open,
+ .close = nvhdmi_dig_playback_pcm_close,
+ .prepare = nvhdmi_dig_playback_pcm_prepare
+ },
+};
+
+static int nvhdmi_build_pcms(struct hda_codec *codec)
+{
+ struct nvhdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = &spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "NVIDIA HDMI";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
+
+ return 0;
+}
+
+static void nvhdmi_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
+
+static struct hda_codec_ops nvhdmi_patch_ops = {
+ .build_controls = nvhdmi_build_controls,
+ .build_pcms = nvhdmi_build_pcms,
+ .init = nvhdmi_init,
+ .free = nvhdmi_free,
+};
+
+static int patch_nvhdmi(struct hda_codec *codec)
+{
+ struct nvhdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
+ * seems to be unused in pure-digital
+ * case. */
+
+ codec->patch_ops = nvhdmi_patch_ops;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
+ { .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi },
+ { .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi },
+ {} /* terminator */
+};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b0a2a262ece..e72707cb60a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -72,6 +72,7 @@ enum {
enum {
ALC260_BASIC,
ALC260_HP,
+ ALC260_HP_DC7600,
ALC260_HP_3013,
ALC260_FUJITSU_S702X,
ALC260_ACER,
@@ -100,6 +101,9 @@ enum {
ALC262_BENQ_T31,
ALC262_ULTRA,
ALC262_LENOVO_3000,
+ ALC262_NEC,
+ ALC262_TOSHIBA_S06,
+ ALC262_TOSHIBA_RX1,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
};
@@ -110,6 +114,7 @@ enum {
ALC268_3ST,
ALC268_TOSHIBA,
ALC268_ACER,
+ ALC268_ACER_ASPIRE_ONE,
ALC268_DELL,
ALC268_ZEPTO,
#ifdef CONFIG_SND_DEBUG
@@ -122,6 +127,9 @@ enum {
/* ALC269 models */
enum {
ALC269_BASIC,
+ ALC269_QUANTA_FL1,
+ ALC269_ASUS_EEEPC_P703,
+ ALC269_ASUS_EEEPC_P901,
ALC269_AUTO,
ALC269_MODEL_LAST /* last tag */
};
@@ -163,6 +171,17 @@ enum {
ALC662_LENOVO_101E,
ALC662_ASUS_EEEPC_P701,
ALC662_ASUS_EEEPC_EP20,
+ ALC663_ASUS_M51VA,
+ ALC663_ASUS_G71V,
+ ALC663_ASUS_H13,
+ ALC663_ASUS_G50V,
+ ALC662_ECS,
+ ALC663_ASUS_MODE1,
+ ALC662_ASUS_MODE2,
+ ALC663_ASUS_MODE3,
+ ALC663_ASUS_MODE4,
+ ALC663_ASUS_MODE5,
+ ALC663_ASUS_MODE6,
ALC662_AUTO,
ALC662_MODEL_LAST,
};
@@ -194,17 +213,21 @@ enum {
ALC883_ACER,
ALC883_ACER_ASPIRE,
ALC883_MEDION,
- ALC883_MEDION_MD2,
+ ALC883_MEDION_MD2,
ALC883_LAPTOP_EAPD,
ALC883_LENOVO_101E_2ch,
ALC883_LENOVO_NB0763,
ALC888_LENOVO_MS7195_DIG,
- ALC883_HAIER_W66,
+ ALC888_LENOVO_SKY,
+ ALC883_HAIER_W66,
ALC888_3ST_HP,
ALC888_6ST_DELL,
ALC883_MITAC,
ALC883_CLEVO_M720,
ALC883_FUJITSU_PI2515,
+ ALC883_3ST_6ch_INTEL,
+ ALC888_ASUS_M90V,
+ ALC888_ASUS_EEE1601,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -280,6 +303,10 @@ struct alc_spec {
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
+
+ /* for PLL fix */
+ hda_nid_t pll_nid;
+ unsigned int pll_coef_idx, pll_coef_bit;
};
/*
@@ -387,7 +414,7 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
/*
* Control the mode of pin widget settings via the mixer. "pc" is used
- * instead of "%" to avoid consequences of accidently treating the % as
+ * instead of "%" to avoid consequences of accidently treating the % as
* being part of a format specifier. Maximum allowed length of a value is
* 63 characters plus NULL terminator.
*
@@ -418,7 +445,7 @@ static unsigned char alc_pin_mode_values[] = {
#define ALC_PIN_DIR_IN_NOMICBIAS 0x03
#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
-/* Info about the pin modes supported by the different pin direction modes.
+/* Info about the pin modes supported by the different pin direction modes.
* For each direction the minimum and maximum values are given.
*/
static signed char alc_pin_mode_dir_info[5][2] = {
@@ -491,7 +518,7 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
AC_VERB_SET_PIN_WIDGET_CONTROL,
alc_pin_mode_values[val]);
- /* Also enable the retasking pin's input/output as required
+ /* Also enable the retasking pin's input/output as required
* for the requested pin mode. Enum values of 2 or less are
* input modes.
*
@@ -696,7 +723,7 @@ static void setup_preset(struct alc_spec *spec,
i++)
spec->init_verbs[spec->num_init_verbs++] =
preset->init_verbs[i];
-
+
spec->channel_mode = preset->channel_mode;
spec->num_channel_mode = preset->num_channel_mode;
spec->need_dac_fix = preset->need_dac_fix;
@@ -707,7 +734,7 @@ static void setup_preset(struct alc_spec *spec,
spec->multiout.dac_nids = preset->dac_nids;
spec->multiout.dig_out_nid = preset->dig_out_nid;
spec->multiout.hp_nid = preset->hp_nid;
-
+
spec->num_mux_defs = preset->num_mux_defs;
if (!spec->num_mux_defs)
spec->num_mux_defs = 1;
@@ -747,6 +774,38 @@ static struct hda_verb alc_gpio3_init_verbs[] = {
{ }
};
+/*
+ * Fix hardware PLL issue
+ * On some codecs, the analog PLL gating control must be off while
+ * the default value is 1.
+ */
+static void alc_fix_pll(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val;
+
+ if (!spec->pll_nid)
+ return;
+ snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
+ spec->pll_coef_idx);
+ val = snd_hda_codec_read(codec, spec->pll_nid, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
+ spec->pll_coef_idx);
+ snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF,
+ val & ~(1 << spec->pll_coef_bit));
+}
+
+static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_bit)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->pll_nid = nid;
+ spec->pll_coef_idx = coef_idx;
+ spec->pll_coef_bit = coef_bit;
+ alc_fix_pll(codec);
+}
+
static void alc_sku_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -763,6 +822,27 @@ static void alc_sku_automute(struct hda_codec *codec)
spec->jack_present ? 0 : PIN_OUT);
}
+static void alc_mic_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int present;
+ unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
+ unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
+ unsigned int mix_nid = spec->capsrc_nids[0];
+ unsigned int capsrc_idx_mic, capsrc_idx_fmic;
+
+ capsrc_idx_mic = mic_nid - 0x18;
+ capsrc_idx_fmic = fmic_nid - 0x18;
+ present = snd_hda_codec_read(codec, mic_nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80));
+ snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0));
+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
/* unsolicited event for HP jack sensing */
static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
{
@@ -770,10 +850,35 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
res >>= 28;
else
res >>= 26;
- if (res != ALC880_HP_EVENT)
- return;
+ if (res == ALC880_HP_EVENT)
+ alc_sku_automute(codec);
+ if (res == ALC880_MIC_EVENT)
+ alc_mic_automute(codec);
+}
+
+static void alc_inithook(struct hda_codec *codec)
+{
alc_sku_automute(codec);
+ alc_mic_automute(codec);
+}
+
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0);
+ tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+ if ((tmp & 0xf0) == 2)
+ /* alc888S-VC */
+ snd_hda_codec_read(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x830);
+ else
+ /* alc888-VB */
+ snd_hda_codec_read(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x3030);
}
/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
@@ -794,7 +899,7 @@ static void alc_subsystem_id(struct hda_codec *codec,
if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
goto do_sku;
- /*
+ /*
* 31~30 : port conetcivity
* 29~21 : reserve
* 20 : PCBEEP input
@@ -851,8 +956,10 @@ do_sku:
case 0x10ec0267:
case 0x10ec0268:
case 0x10ec0269:
+ case 0x10ec0660:
+ case 0x10ec0662:
+ case 0x10ec0663:
case 0x10ec0862:
- case 0x10ec0662:
case 0x10ec0889:
snd_hda_codec_write(codec, 0x14, 0,
AC_VERB_SET_EAPD_BTLENABLE, 2);
@@ -877,18 +984,20 @@ do_sku:
case 0x10ec0882:
case 0x10ec0883:
case 0x10ec0885:
- case 0x10ec0888:
case 0x10ec0889:
snd_hda_codec_write(codec, 0x20, 0,
AC_VERB_SET_COEF_INDEX, 7);
tmp = snd_hda_codec_read(codec, 0x20, 0,
AC_VERB_GET_PROC_COEF, 0);
snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
+ AC_VERB_SET_COEF_INDEX, 7);
snd_hda_codec_write(codec, 0x20, 0,
AC_VERB_SET_PROC_COEF,
tmp | 0x2010);
break;
+ case 0x10ec0888:
+ /*alc888_coef_init(codec);*/ /* called in alc_init() */
+ break;
case 0x10ec0267:
case 0x10ec0268:
snd_hda_codec_write(codec, 0x20, 0,
@@ -896,7 +1005,7 @@ do_sku:
tmp = snd_hda_codec_read(codec, 0x20, 0,
AC_VERB_GET_PROC_COEF, 0);
snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
+ AC_VERB_SET_COEF_INDEX, 7);
snd_hda_codec_write(codec, 0x20, 0,
AC_VERB_SET_PROC_COEF,
tmp | 0x3000);
@@ -905,7 +1014,7 @@ do_sku:
default:
break;
}
-
+
/* is laptop or Desktop and enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
@@ -937,10 +1046,18 @@ do_sku:
else
return;
}
+ if (spec->autocfg.hp_pins[0])
+ snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | ALC880_HP_EVENT);
+
+ if (spec->autocfg.input_pins[AUTO_PIN_MIC] &&
+ spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC])
+ snd_hda_codec_write(codec,
+ spec->autocfg.input_pins[AUTO_PIN_MIC], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | ALC880_MIC_EVENT);
- snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_HP_EVENT);
spec->unsol_event = alc_sku_unsol_event;
}
@@ -1231,7 +1348,7 @@ static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
*
* The system also has a pair of internal speakers, and a headphone jack.
* These are both connected to Line2 on the codec, hence to DAC 02.
- *
+ *
* There is a variable resistor to control the speaker or headphone
* volume. This is a hardware-only device without a software API.
*
@@ -1759,7 +1876,7 @@ static struct hda_verb alc880_pin_6stack_init_verbs[] = {
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
+
{ }
};
@@ -1804,7 +1921,7 @@ static struct hda_verb alc880_uniwill_init_verbs[] = {
/*
* Uniwill P53
-* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
+* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
*/
static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -1903,7 +2020,7 @@ static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec)
static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
{
unsigned int present;
-
+
present = snd_hda_codec_read(codec, 0x21, 0,
AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
present &= HDA_AMP_VOLMASK;
@@ -1985,7 +2102,7 @@ static struct hda_verb alc880_pin_asus_init_verbs[] = {
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
+
{ }
};
@@ -2373,6 +2490,10 @@ static int alc_init(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
unsigned int i;
+ alc_fix_pll(codec);
+ if (codec->vendor_id == 0x10ec0888)
+ alc888_coef_init(codec);
+
for (i = 0; i < spec->num_init_verbs; i++)
snd_hda_sequence_write(codec, spec->init_verbs[i]);
@@ -2563,12 +2684,14 @@ static int alc_build_pcms(struct hda_codec *codec)
info->name = spec->stream_name_analog;
if (spec->stream_analog_playback) {
- snd_assert(spec->multiout.dac_nids, return -EINVAL);
+ if (snd_BUG_ON(!spec->multiout.dac_nids))
+ return -EINVAL;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
}
if (spec->stream_analog_capture) {
- snd_assert(spec->adc_nids, return -EINVAL);
+ if (snd_BUG_ON(!spec->adc_nids))
+ return -EINVAL;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
}
@@ -2598,6 +2721,8 @@ static int alc_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
}
+ /* FIXME: do we need this for all Realtek codec models? */
+ codec->spdif_status_reset = 1;
}
/* If the use of more than one ADC is requested for the current
@@ -3009,6 +3134,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+ SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU),
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),
@@ -3613,7 +3739,7 @@ static void alc880_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 < spec->autocfg.line_outs; i++) {
hda_nid_t nid = spec->autocfg.line_out_pins[i];
@@ -3717,7 +3843,7 @@ static void alc880_auto_init(struct hda_codec *codec)
alc880_auto_init_extra_out(codec);
alc880_auto_init_analog_input(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
/*
@@ -4054,6 +4180,33 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
{ } /* end */
};
+static struct hda_bind_ctls alc260_dc7600_bind_master_vol = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls alc260_dc7600_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol),
+ HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 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},
{},
@@ -4077,7 +4230,30 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
alc260_hp_3013_automute(codec);
}
-/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
+static void alc260_hp_3012_automute(struct hda_codec *codec)
+{
+ unsigned int present, bits;
+
+ present = snd_hda_codec_read(codec, 0x10, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+
+ bits = present ? 0 : PIN_OUT;
+ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+ snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+}
+
+static void alc260_hp_3012_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc260_hp_3012_automute(codec);
+}
+
+/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
* HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
*/
static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
@@ -4408,7 +4584,7 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {
{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
- /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
+ /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
* when acting as an output.
*/
{0x0d, AC_VERB_SET_CONNECT_SEL, 0},
@@ -4433,14 +4609,14 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {
* stage.
*/
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute input buffer of pin widget used for Line-in (no equiv
+ /* Unmute input buffer of pin widget used for Line-in (no equiv
* mixer ctrl)
*/
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Mute capture amp left and right */
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - line
+ /* Set ADC connection select to match default mixer setting - line
* in (on mic1 pin)
*/
{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -4494,7 +4670,7 @@ static struct hda_verb alc260_acer_init_verbs[] = {
{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
- /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
+ /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
* bus when acting as outputs.
*/
{0x0b, AC_VERB_SET_CONNECT_SEL, 0},
@@ -4605,6 +4781,20 @@ static void alc260_replacer_672v_unsol_event(struct hda_codec *codec,
alc260_replacer_672v_automute(codec);
}
+static struct hda_verb alc260_hp_dc7600_verbs[] = {
+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
/* Test configuration for debugging, modelled after the ALC880 test
* configuration.
*/
@@ -4616,7 +4806,7 @@ static hda_nid_t alc260_test_adc_nids[2] = {
0x04, 0x05,
};
/* For testing the ALC260, each input MUX needs its own definition since
- * the signal assignments are different. This assumes that the first ADC
+ * the signal assignments are different. This assumes that the first ADC
* is NID 0x04.
*/
static struct hda_input_mux alc260_test_capture_sources[2] = {
@@ -4699,7 +4889,7 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
/* Switches to allow the digital IO pins to be enabled. The datasheet
* is ambigious as to which NID is which; testing on laptops which
- * make this output available should provide clarification.
+ * make this output available should provide clarification.
*/
ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
@@ -4735,7 +4925,7 @@ static struct hda_verb alc260_test_init_verbs[] = {
{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
- /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the
+ /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the
* OUT1 sum bus when acting as an output.
*/
{0x0b, AC_VERB_SET_CONNECT_SEL, 0},
@@ -4827,7 +5017,7 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
} else
return 0; /* N/A */
-
+
snprintf(name, sizeof(name), "%s Playback Volume", pfx);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);
if (err < 0)
@@ -4933,7 +5123,7 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
int pin_type = get_pin_type(spec->autocfg.line_out_type);
alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0);
}
-
+
nid = spec->autocfg.speaker_pins[0];
if (nid)
alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
@@ -4975,7 +5165,7 @@ static struct hda_verb alc260_volume_init_verbs[] = {
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
+
/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
* mixer widget
* Note: PASD motherboards uses the Line In 2 as the input for
@@ -5004,7 +5194,7 @@ static struct hda_verb alc260_volume_init_verbs[] = {
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
+
{ }
};
@@ -5064,7 +5254,7 @@ static void alc260_auto_init(struct hda_codec *codec)
alc260_auto_init_multi_out(codec);
alc260_auto_init_analog_input(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -5085,6 +5275,7 @@ static const char *alc260_models[ALC260_MODEL_LAST] = {
[ALC260_BASIC] = "basic",
[ALC260_HP] = "hp",
[ALC260_HP_3013] = "hp-3013",
+ [ALC260_HP_DC7600] = "hp-dc7600",
[ALC260_FUJITSU_S702X] = "fujitsu",
[ALC260_ACER] = "acer",
[ALC260_WILL] = "will",
@@ -5101,8 +5292,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013),
+ SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
+ SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),
SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP),
@@ -5148,6 +5339,22 @@ static struct alc_config_preset alc260_presets[] = {
.unsol_event = alc260_hp_unsol_event,
.init_hook = alc260_hp_automute,
},
+ [ALC260_HP_DC7600] = {
+ .mixers = { alc260_hp_dc7600_mixer,
+ alc260_input_mixer,
+ alc260_capture_alt_mixer },
+ .init_verbs = { alc260_init_verbs,
+ alc260_hp_dc7600_verbs },
+ .num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ .dac_nids = alc260_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+ .adc_nids = alc260_hp_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc260_modes),
+ .channel_mode = alc260_modes,
+ .input_mux = &alc260_capture_source,
+ .unsol_event = alc260_hp_3012_unsol_event,
+ .init_hook = alc260_hp_3012_automute,
+ },
[ALC260_HP_3013] = {
.mixers = { alc260_hp_3013_mixer,
alc260_input_mixer,
@@ -5863,7 +6070,7 @@ static struct hda_verb alc882_targa_verbs[] = {
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
+
{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -5879,7 +6086,7 @@ static struct hda_verb alc882_targa_verbs[] = {
static void alc882_targa_automute(struct hda_codec *codec)
{
unsigned int present;
-
+
present = snd_hda_codec_read(codec, 0x14, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
@@ -5905,7 +6112,7 @@ static struct hda_verb alc882_asus_a7j_verbs[] = {
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
+
{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
{0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
@@ -5923,7 +6130,7 @@ static struct hda_verb alc882_asus_a7m_verbs[] = {
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
+
{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
{0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
@@ -6249,7 +6456,7 @@ static struct alc_config_preset alc882_presets[] = {
.channel_mode = alc882_3ST_6ch_modes,
.need_dac_fix = 1,
.input_mux = &alc882_capture_source,
- },
+ },
[ALC882_ASUS_A7M] = {
.mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_init_verbs, alc882_eapd_verbs,
@@ -6262,14 +6469,14 @@ static struct alc_config_preset alc882_presets[] = {
.channel_mode = alc880_threestack_modes,
.need_dac_fix = 1,
.input_mux = &alc882_capture_source,
- },
+ },
};
/*
* Pin config fixes
*/
-enum {
+enum {
PINFIX_ABIT_AW9D_MAX
};
@@ -6353,7 +6560,9 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec)
continue;
vref = PIN_IN;
if (1 /*i <= AUTO_PIN_FRONT_MIC*/) {
- if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) &
+ unsigned int pincap;
+ pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ if ((pincap >> AC_PINCAP_VREF_SHIFT) &
AC_PINCAP_VREF_80)
vref = PIN_VREF80;
}
@@ -6366,6 +6575,39 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec)
}
}
+static void alc882_auto_init_input_src(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = spec->input_mux;
+ int c;
+
+ for (c = 0; c < spec->num_adc_nids; c++) {
+ hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
+ hda_nid_t nid = spec->capsrc_nids[c];
+ int conns, mute, idx, item;
+
+ conns = snd_hda_get_connections(codec, nid, conn_list,
+ ARRAY_SIZE(conn_list));
+ if (conns < 0)
+ continue;
+ for (idx = 0; idx < conns; idx++) {
+ /* if the current connection is the selected one,
+ * unmute it as default - otherwise mute it
+ */
+ mute = AMP_IN_MUTE(idx);
+ for (item = 0; item < imux->num_items; item++) {
+ if (imux->items[item].index == idx) {
+ if (spec->cur_mux[c] == item)
+ mute = AMP_IN_UNMUTE(idx);
+ break;
+ }
+ }
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, mute);
+ }
+ }
+}
+
/* add mic boosts if needed */
static int alc_auto_add_mic_boost(struct hda_codec *codec)
{
@@ -6420,8 +6662,9 @@ static void alc882_auto_init(struct hda_codec *codec)
alc882_auto_init_multi_out(codec);
alc882_auto_init_hp_out(codec);
alc882_auto_init_analog_input(codec);
+ alc882_auto_init_input_src(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */
@@ -6448,15 +6691,19 @@ static int patch_alc882(struct hda_codec *codec)
board_config = ALC885_MACPRO;
break;
case 0x106b1000: /* iMac 24 */
+ case 0x106b2800: /* AppleTV */
board_config = ALC885_IMAC24;
break;
- case 0x106b00a1: /* Macbook */
+ case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
+ case 0x106b00a4: /* MacbookPro4,1 */
case 0x106b2c00: /* Macbook Pro rev3 */
+ case 0x106b3600: /* Macbook 3.1 */
board_config = ALC885_MBP3;
break;
default:
/* ALC889A is handled better as ALC888-compatible */
- if (codec->revision_id == 0x100103) {
+ if (codec->revision_id == 0x100101 ||
+ codec->revision_id == 0x100103) {
alc_free(codec);
return patch_alc883(codec);
}
@@ -6485,14 +6732,20 @@ static int patch_alc882(struct hda_codec *codec)
if (board_config != ALC882_AUTO)
setup_preset(spec, &alc882_presets[board_config]);
- spec->stream_name_analog = "ALC882 Analog";
+ if (codec->vendor_id == 0x10ec0885) {
+ spec->stream_name_analog = "ALC885 Analog";
+ spec->stream_name_digital = "ALC885 Digital";
+ } else {
+ spec->stream_name_analog = "ALC882 Analog";
+ spec->stream_name_digital = "ALC882 Digital";
+ }
+
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;
spec->stream_digital_capture = &alc882_pcm_digital_capture;
@@ -6569,6 +6822,16 @@ static struct hda_input_mux alc883_capture_source = {
},
};
+static struct hda_input_mux alc883_3stack_6ch_intel = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x1 },
+ { "Front Mic", 0x0 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
static struct hda_input_mux alc883_lenovo_101e_capture_source = {
.num_items = 2,
.items = {
@@ -6595,6 +6858,23 @@ static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
},
};
+static struct hda_input_mux alc883_lenovo_sky_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_asus_eee1601_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Line", 0x2 },
+ },
+};
+
#define alc883_mux_enum_info alc_mux_enum_info
#define alc883_mux_enum_get alc_mux_enum_get
/* ALC883 has the ALC882-type input selection */
@@ -6650,6 +6930,48 @@ static struct hda_channel_mode alc883_3ST_6ch_modes[3] = {
};
/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_intel_init[] = {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_intel_init[] = {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_intel_init[] = {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
+ { 2, alc883_3ST_ch2_intel_init },
+ { 4, alc883_3ST_ch4_intel_init },
+ { 6, alc883_3ST_ch6_intel_init },
+};
+
+/*
* 6ch mode
*/
static struct hda_verb alc883_sixstack_ch6_init[] = {
@@ -6867,6 +7189,43 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_6ch_intel_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", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, 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),
{
@@ -6883,13 +7242,13 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 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", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
@@ -7052,7 +7411,7 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
.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),
@@ -7079,6 +7438,87 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc888_lenovo_sky_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_MUTE("iSpeaker Playback Switch", 0x1a, 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("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 hda_bind_ctls alc883_bind_cap_vol = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls alc883_bind_cap_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
+ HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc883_chmode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -7092,7 +7532,7 @@ static struct snd_kcontrol_new alc883_chmode_mixer[] = {
static struct hda_verb alc883_init_verbs[] = {
/* ADC1: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
/* ADC2: mute amp left and right */
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -7157,14 +7597,14 @@ static struct hda_verb alc883_init_verbs[] = {
/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
/* Input mixer2 */
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
/* Input mixer3 */
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
{ }
};
@@ -7264,7 +7704,7 @@ static struct hda_verb alc883_tagra_verbs[] = {
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
+
{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -7314,6 +7754,18 @@ static struct hda_verb alc883_haier_w66_verbs[] = {
{ } /* end */
};
+static struct hda_verb alc888_lenovo_sky_verbs[] = {
+ {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)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { } /* end */
+};
+
static struct hda_verb alc888_3st_hp_verbs[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
{0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
@@ -7351,7 +7803,7 @@ static struct hda_channel_mode alc888_3st_hp_modes[2] = {
static void alc888_lenovo_ms7195_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,
@@ -7364,7 +7816,7 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
{
unsigned int present;
-
+
present = snd_hda_codec_read(codec, 0x14, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -7394,7 +7846,7 @@ static struct hda_verb alc883_medion_md2_verbs[] = {
static void alc883_medion_md2_automute(struct hda_codec *codec)
{
unsigned int present;
-
+
present = snd_hda_codec_read(codec, 0x14, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -7549,7 +8001,7 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
static void alc883_acer_aspire_automute(struct hda_codec *codec)
{
unsigned int present;
-
+
present = snd_hda_codec_read(codec, 0x14, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -7586,7 +8038,7 @@ 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,
@@ -7610,6 +8062,50 @@ static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
}
}
+static void alc888_lenovo_sky_front_automute(struct hda_codec *codec)
+{
+ unsigned int mute;
+ unsigned int present;
+
+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present = (present & 0x80000000) != 0;
+ if (present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+static void alc883_lenovo_sky_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc888_lenovo_sky_front_automute(codec);
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -7694,6 +8190,105 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
{ } /* end */
};
+static struct hda_verb alc888_asus_m90v_verbs[] = {
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* enable unsolicited event */
+ {0x1b, 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 void alc883_nb_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+ snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
+}
+
+static void alc883_M90V_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? 0 : PIN_OUT;
+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+ snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+}
+
+static void alc883_mode2_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc883_M90V_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc883_nb_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc883_mode2_inithook(struct hda_codec *codec)
+{
+ alc883_M90V_speaker_automute(codec);
+ alc883_nb_mic_automute(codec);
+}
+
+static struct hda_verb alc888_asus_eee1601_verbs[] = {
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0838},
+ /* enable unsolicited event */
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { } /* end */
+};
+
+static void alc883_eee1601_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? 0 : PIN_OUT;
+ snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ bits);
+}
+
+static void alc883_eee1601_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc883_eee1601_speaker_automute(codec);
+ break;
+ }
+}
+
+static void alc883_eee1601_inithook(struct hda_codec *codec)
+{
+ alc883_eee1601_speaker_automute(codec);
+}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc883_loopbacks alc880_loopbacks
#endif
@@ -7723,12 +8318,14 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC883_LENOVO_101E_2ch] = "lenovo-101e",
[ALC883_LENOVO_NB0763] = "lenovo-nb0763",
[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
+ [ALC888_LENOVO_SKY] = "lenovo-sky",
[ALC883_HAIER_W66] = "haier-w66",
[ALC888_3ST_HP] = "3stack-hp",
[ALC888_6ST_DELL] = "6stack-dell",
[ALC883_MITAC] = "mitac",
[ALC883_CLEVO_M720] = "clevo-m720",
[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
+ [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
[ALC883_AUTO] = "auto",
};
@@ -7737,17 +8334,21 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
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, 0x0121, "Acer Aspire 5920G", 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(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_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(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
@@ -7783,9 +8384,12 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+ SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY),
SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
+ SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
+ SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
{}
};
@@ -7824,6 +8428,18 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
},
+ [ALC883_3ST_6ch_INTEL] = {
+ .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes),
+ .channel_mode = alc883_3ST_6ch_intel_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_3stack_6ch_intel,
+ },
[ALC883_6ST_DIG] = {
.mixers = { alc883_base_mixer, alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs },
@@ -7908,7 +8524,7 @@ static struct alc_config_preset alc883_presets[] = {
.input_mux = &alc883_capture_source,
.unsol_event = alc883_medion_md2_unsol_event,
.init_hook = alc883_medion_md2_automute,
- },
+ },
[ALC883_LAPTOP_EAPD] = {
.mixers = { alc883_base_mixer },
.init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
@@ -8025,6 +8641,49 @@ static struct alc_config_preset alc883_presets[] = {
.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
.init_hook = alc883_2ch_fujitsu_pi2515_automute,
},
+ [ALC888_LENOVO_SKY] = {
+ .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_lenovo_sky_capture_source,
+ .unsol_event = alc883_lenovo_sky_unsol_event,
+ .init_hook = alc888_lenovo_sky_front_automute,
+ },
+ [ALC888_ASUS_M90V] = {
+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+ .channel_mode = alc883_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_fujitsu_pi2515_capture_source,
+ .unsol_event = alc883_mode2_unsol_event,
+ .init_hook = alc883_mode2_inithook,
+ },
+ [ALC888_ASUS_EEE1601] = {
+ .mixers = { alc883_asus_eee1601_mixer },
+ .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_asus_eee1601_capture_source,
+ .unsol_event = alc883_eee1601_unsol_event,
+ .init_hook = alc883_eee1601_inithook,
+ },
};
@@ -8100,6 +8759,8 @@ static void alc883_auto_init_analog_input(struct hda_codec *codec)
}
}
+#define alc883_auto_init_input_src alc882_auto_init_input_src
+
/* almost identical with ALC880 parser... */
static int alc883_parse_auto_config(struct hda_codec *codec)
{
@@ -8130,8 +8791,9 @@ static void alc883_auto_init(struct hda_codec *codec)
alc883_auto_init_multi_out(codec);
alc883_auto_init_hp_out(codec);
alc883_auto_init_analog_input(codec);
+ alc883_auto_init_input_src(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
static int patch_alc883(struct hda_codec *codec)
@@ -8145,6 +8807,8 @@ static int patch_alc883(struct hda_codec *codec)
codec->spec = spec;
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
alc883_models,
alc883_cfg_tbl);
@@ -8171,12 +8835,30 @@ static int patch_alc883(struct hda_codec *codec)
if (board_config != ALC883_AUTO)
setup_preset(spec, &alc883_presets[board_config]);
- spec->stream_name_analog = "ALC883 Analog";
+ switch (codec->vendor_id) {
+ case 0x10ec0888:
+ if (codec->revision_id == 0x100101) {
+ spec->stream_name_analog = "ALC1200 Analog";
+ spec->stream_name_digital = "ALC1200 Digital";
+ } else {
+ spec->stream_name_analog = "ALC888 Analog";
+ spec->stream_name_digital = "ALC888 Digital";
+ }
+ break;
+ case 0x10ec0889:
+ spec->stream_name_analog = "ALC889 Analog";
+ spec->stream_name_digital = "ALC889 Digital";
+ break;
+ default:
+ spec->stream_name_analog = "ALC883 Analog";
+ spec->stream_name_digital = "ALC883 Digital";
+ break;
+ }
+
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;
spec->stream_digital_capture = &alc883_pcm_digital_capture;
@@ -8189,6 +8871,7 @@ static int patch_alc883(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
if (board_config == ALC883_AUTO)
spec->init_hook = alc883_auto_init;
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc883_loopbacks;
@@ -8213,6 +8896,13 @@ static int patch_alc883(struct hda_codec *codec)
#define alc262_modes alc260_modes
#define alc262_capture_source alc882_capture_source
+static hda_nid_t alc262_dmic_adc_nids[1] = {
+ /* ADC0 */
+ 0x09
+};
+
+static hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 };
+
static struct snd_kcontrol_new alc262_base_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -8594,10 +9284,10 @@ static struct hda_verb alc262_init_verbs[] = {
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
+
{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-
+
/* FIXME: use matrix-type input source selection */
/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
@@ -8619,6 +9309,12 @@ static struct hda_verb alc262_init_verbs[] = {
{ }
};
+static struct hda_verb alc262_eapd_verbs[] = {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ { }
+};
+
static struct hda_verb alc262_hippo_unsol_verbs[] = {
{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -8645,6 +9341,91 @@ static struct hda_verb alc262_sony_unsol_verbs[] = {
{}
};
+static struct hda_input_mux alc262_dmic_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Int DMic", 0x9 },
+ { "Mic", 0x0 },
+ },
+};
+
+static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 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("Capture Volume", 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb alc262_toshiba_s06_verbs[] = {
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x22, AC_VERB_SET_CONNECT_SEL, 0x09},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static void alc262_dmic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x22, 0,
+ AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
+}
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc262_toshiba_s06_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? 0 : PIN_OUT;
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
+}
+
+
+
+/* unsolicited event for HP jack sensing */
+static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc262_toshiba_s06_speaker_automute(codec);
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc262_dmic_automute(codec);
+
+}
+
+static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
+{
+ alc262_toshiba_s06_speaker_automute(codec);
+ alc262_dmic_automute(codec);
+}
+
/* mute/unmute internal speaker according to the hp jack and mute state */
static void alc262_hippo_automute(struct hda_codec *codec)
{
@@ -8709,6 +9490,41 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec,
}
/*
+ * nec model
+ * 0x15 = headphone
+ * 0x16 = internal speaker
+ * 0x18 = external mic
+ */
+
+static struct snd_kcontrol_new alc262_nec_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 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),
+
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_nec_verbs[] = {
+ /* Unmute Speaker */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Headphone */
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* External mic to headphone */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* External mic to speaker */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {}
+};
+
+/*
* fujitsu model
* 0x14 = headphone/spdif-out, 0x15 = internal speaker,
* 0x1b = port replicator headphone out
@@ -8940,6 +9756,25 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc262_sony_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, 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),
+ 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),
+ { } /* end */
+};
+
/* additional init verbs for Benq laptops */
static struct hda_verb alc262_EAPD_verbs[] = {
{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
@@ -9188,7 +10023,7 @@ static struct hda_verb alc262_volume_init_verbs[] = {
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
+
/* set up input amps for analog loopback */
/* Amp Indices: DAC = 0, mixer = 1 */
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -9243,7 +10078,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-
+
/*
* Set up output mixers (0x0c - 0x0e)
*/
@@ -9404,6 +10239,24 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
{ }
};
+static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
+
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
+
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc262_loopbacks alc880_loopbacks
#endif
@@ -9460,6 +10313,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
#define alc262_auto_init_multi_out alc882_auto_init_multi_out
#define alc262_auto_init_hp_out alc882_auto_init_hp_out
#define alc262_auto_init_analog_input alc882_auto_init_analog_input
+#define alc262_auto_init_input_src alc882_auto_init_input_src
/* init callback for auto-configuration model -- overriding the default init */
@@ -9469,8 +10323,9 @@ static void alc262_auto_init(struct hda_codec *codec)
alc262_auto_init_multi_out(codec);
alc262_auto_init_hp_out(codec);
alc262_auto_init_analog_input(codec);
+ alc262_auto_init_input_src(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
/*
@@ -9488,13 +10343,17 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {
[ALC262_BENQ_ED8] = "benq",
[ALC262_BENQ_T31] = "benq-t31",
[ALC262_SONY_ASSAMD] = "sony-assamd",
+ [ALC262_TOSHIBA_S06] = "toshiba-s06",
+ [ALC262_TOSHIBA_RX1] = "toshiba-rx1",
[ALC262_ULTRA] = "ultra",
[ALC262_LENOVO_3000] = "lenovo-3000",
+ [ALC262_NEC] = "nec",
[ALC262_AUTO] = "auto",
};
static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
+ SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC),
SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
@@ -9522,6 +10381,9 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
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(0x1179, 0x0001, "Toshiba dynabook SS RX1",
+ ALC262_TOSHIBA_RX1),
+ SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
@@ -9675,7 +10537,7 @@ static struct alc_config_preset alc262_presets[] = {
.input_mux = &alc262_capture_source,
.unsol_event = alc262_hippo_unsol_event,
.init_hook = alc262_hippo_automute,
- },
+ },
[ALC262_ULTRA] = {
.mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
.init_verbs = { alc262_ultra_verbs },
@@ -9703,6 +10565,43 @@ static struct alc_config_preset alc262_presets[] = {
.input_mux = &alc262_fujitsu_capture_source,
.unsol_event = alc262_lenovo_3000_unsol_event,
},
+ [ALC262_NEC] = {
+ .mixers = { alc262_nec_mixer },
+ .init_verbs = { alc262_nec_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,
+ },
+ [ALC262_TOSHIBA_S06] = {
+ .mixers = { alc262_toshiba_s06_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs,
+ alc262_eapd_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .capsrc_nids = alc262_dmic_capsrc_nids,
+ .dac_nids = alc262_dac_nids,
+ .adc_nids = alc262_dmic_adc_nids, /* ADC0 */
+ .dig_out_nid = ALC262_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_dmic_capture_source,
+ .unsol_event = alc262_toshiba_s06_unsol_event,
+ .init_hook = alc262_toshiba_s06_init_hook,
+ },
+ [ALC262_TOSHIBA_RX1] = {
+ .mixers = { alc262_toshiba_rx1_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_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_hippo_unsol_event,
+ .init_hook = alc262_hippo_automute,
+ },
};
static int patch_alc262(struct hda_codec *codec)
@@ -9729,6 +10628,8 @@ static int patch_alc262(struct hda_codec *codec)
}
#endif
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
alc262_models,
alc262_cfg_tbl);
@@ -9759,7 +10660,7 @@ static int patch_alc262(struct hda_codec *codec)
spec->stream_name_analog = "ALC262 Analog";
spec->stream_analog_playback = &alc262_pcm_analog_playback;
spec->stream_analog_capture = &alc262_pcm_analog_capture;
-
+
spec->stream_name_digital = "ALC262 Digital";
spec->stream_digital_playback = &alc262_pcm_digital_playback;
spec->stream_digital_capture = &alc262_pcm_digital_capture;
@@ -9795,7 +10696,7 @@ static int patch_alc262(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc262_loopbacks;
#endif
-
+
return 0;
}
@@ -9804,7 +10705,7 @@ static int patch_alc262(struct hda_codec *codec)
*/
#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID
#define alc268_modes alc260_modes
-
+
static hda_nid_t alc268_dac_nids[2] = {
/* front, hp */
0x02, 0x03
@@ -9864,6 +10765,14 @@ static struct hda_verb alc268_toshiba_verbs[] = {
{ } /* end */
};
+static struct hda_input_mux alc268_acer_lc_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "i-Mic", 0x6 },
+ { "E-Mic", 0x0 },
+ },
+};
+
/* Acer specific */
/* bind volumes of both NID 0x02 and 0x03 */
static struct hda_bind_ctls alc268_acer_bind_master_vol = {
@@ -9916,6 +10825,21 @@ static int alc268_acer_master_sw_put(struct snd_kcontrol *kcontrol,
return change;
}
+static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
+ /* output mixer control */
+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_acer_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ },
+ HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT),
+ { }
+};
+
static struct snd_kcontrol_new alc268_acer_mixer[] = {
/* output mixer control */
HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
@@ -9933,6 +10857,16 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
{ }
};
+static struct hda_verb alc268_acer_aspire_one_verbs[] = {
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x06},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017},
+ { }
+};
+
static struct hda_verb alc268_acer_verbs[] = {
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -9940,7 +10874,6 @@ static struct hda_verb alc268_acer_verbs[] = {
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
{ }
};
@@ -9967,6 +10900,47 @@ static void alc268_acer_init_hook(struct hda_codec *codec)
alc268_acer_automute(codec, 1);
}
+/* toggle speaker-output according to the hp-jack state */
+static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? AMP_IN_MUTE(0) : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+}
+
+
+static void alc268_acer_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
+ present ? 0x0 : 0x6);
+}
+
+static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc268_aspire_one_speaker_automute(codec);
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc268_acer_mic_automute(codec);
+}
+
+static void alc268_acer_lc_init_hook(struct hda_codec *codec)
+{
+ alc268_aspire_one_speaker_automute(codec);
+ alc268_acer_mic_automute(codec);
+}
+
static struct snd_kcontrol_new alc268_dell_mixer[] = {
/* output mixer control */
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
@@ -10115,7 +11089,7 @@ static struct hda_verb alc268_base_init_verbs[] = {
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
/* 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},
@@ -10314,7 +11288,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->line_out_pins[0];
if (nid)
- alc268_new_analog_output(spec, nid, "Front", 0);
+ alc268_new_analog_output(spec, nid, "Front", 0);
nid = cfg->speaker_pins[0];
if (nid == 0x1d) {
@@ -10336,7 +11310,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
if (err < 0)
return err;
}
- return 0;
+ return 0;
}
/* create playback/capture controls for input pins */
@@ -10357,7 +11331,7 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
case 0x1a:
idx1 = 2; /* Line In */
break;
- case 0x1c:
+ case 0x1c:
idx1 = 3; /* CD */
break;
case 0x12:
@@ -10369,7 +11343,7 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
}
imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
imux->items[imux->num_items].index = idx1;
- imux->num_items++;
+ imux->num_items++;
}
return 0;
}
@@ -10399,11 +11373,11 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
}
dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */
- if (line_nid == 0x14)
+ if (line_nid == 0x14)
dac_vol2 = AMP_OUT_ZERO;
else if (line_nid == 0x15)
dac_vol1 = AMP_OUT_ZERO;
- if (hp_nid == 0x14)
+ if (hp_nid == 0x14)
dac_vol2 = AMP_OUT_ZERO;
else if (hp_nid == 0x15)
dac_vol1 = AMP_OUT_ZERO;
@@ -10483,7 +11457,7 @@ static void alc268_auto_init(struct hda_codec *codec)
alc268_auto_init_mono_speaker_out(codec);
alc268_auto_init_analog_input(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
/*
@@ -10494,6 +11468,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
[ALC268_3ST] = "3stack",
[ALC268_TOSHIBA] = "toshiba",
[ALC268_ACER] = "acer",
+ [ALC268_ACER_ASPIRE_ONE] = "acer-aspire",
[ALC268_DELL] = "dell",
[ALC268_ZEPTO] = "zepto",
#ifdef CONFIG_SND_DEBUG
@@ -10508,11 +11483,14 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
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(0x1025, 0x015b, "Acer Aspire One",
+ ALC268_ACER_ASPIRE_ONE),
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(0x1179, 0xff64, "TOSHIBA L305", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
@@ -10585,6 +11563,23 @@ static struct alc_config_preset alc268_presets[] = {
.unsol_event = alc268_acer_unsol_event,
.init_hook = alc268_acer_init_hook,
},
+ [ALC268_ACER_ASPIRE_ONE] = {
+ .mixers = { alc268_acer_aspire_one_mixer,
+ alc268_capture_alt_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_acer_aspire_one_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,
+ .capsrc_nids = alc268_capsrc_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_acer_lc_capture_source,
+ .unsol_event = alc268_acer_lc_unsol_event,
+ .init_hook = alc268_acer_lc_init_hook,
+ },
[ALC268_DELL] = {
.mixers = { alc268_dell_mixer, alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
@@ -10674,12 +11669,18 @@ static int patch_alc268(struct hda_codec *codec)
if (board_config != ALC268_AUTO)
setup_preset(spec, &alc268_presets[board_config]);
- spec->stream_name_analog = "ALC268 Analog";
+ if (codec->vendor_id == 0x10ec0267) {
+ spec->stream_name_analog = "ALC267 Analog";
+ spec->stream_name_digital = "ALC267 Digital";
+ } else {
+ spec->stream_name_analog = "ALC268 Analog";
+ spec->stream_name_digital = "ALC268 Digital";
+ }
+
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 (!query_amp_caps(codec, 0x1d, HDA_INPUT))
@@ -10723,7 +11724,7 @@ static int patch_alc268(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
if (board_config == ALC268_AUTO)
spec->init_hook = alc268_auto_init;
-
+
return 0;
}
@@ -10736,7 +11737,31 @@ static int patch_alc268(struct hda_codec *codec)
static hda_nid_t alc269_adc_nids[1] = {
/* ADC1 */
- 0x07,
+ 0x08,
+};
+
+static hda_nid_t alc269_capsrc_nids[1] = {
+ 0x23,
+};
+
+/* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24),
+ * not a mux!
+ */
+
+static struct hda_input_mux alc269_eeepc_dmic_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "i-Mic", 0x5 },
+ { "e-Mic", 0x0 },
+ },
+};
+
+static struct hda_input_mux alc269_eeepc_amic_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "i-Mic", 0x1 },
+ { "e-Mic", 0x0 },
+ },
};
#define alc269_modes alc260_modes
@@ -10749,6 +11774,8 @@ static struct snd_kcontrol_new alc269_base_mixer[] = {
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("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, 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),
@@ -10758,10 +11785,49 @@ static struct snd_kcontrol_new alc269_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
+ /* output mixer control */
+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_acer_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, 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),
+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ { }
+};
+
+/* bind volumes of both NID 0x0c and 0x0d */
+static struct hda_bind_ctls alc269_epc_bind_vol = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
+ HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_BIND_VOL("LineOut Playback Volume", &alc269_epc_bind_vol),
+ HDA_CODEC_MUTE("LineOut Playback Switch", 0x15, 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),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
@@ -10777,6 +11843,172 @@ static struct snd_kcontrol_new alc269_capture_mixer[] = {
{ } /* end */
};
+/* capture mixer elements */
+static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+/* beep control */
+static struct snd_kcontrol_new alc269_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc269_quanta_fl1_verbs[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ { }
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? AMP_IN_MUTE(0) : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x680);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x480);
+}
+
+static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
+}
+
+static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc269_quanta_fl1_speaker_automute(codec);
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc269_quanta_fl1_mic_automute(codec);
+}
+
+static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
+{
+ alc269_quanta_fl1_speaker_automute(codec);
+ alc269_quanta_fl1_mic_automute(codec);
+}
+
+static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc269_eeepc_amic_init_verbs[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc269_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? AMP_IN_MUTE(0) : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+}
+
+static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, (present ? 0 : 5));
+}
+
+static void alc269_eeepc_amic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+ snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc269_speaker_automute(codec);
+
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc269_eeepc_dmic_automute(codec);
+}
+
+static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
+{
+ alc269_speaker_automute(codec);
+ alc269_eeepc_dmic_automute(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc269_speaker_automute(codec);
+
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc269_eeepc_amic_automute(codec);
+}
+
+static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
+{
+ alc269_speaker_automute(codec);
+ alc269_eeepc_amic_automute(codec);
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -10836,8 +12068,8 @@ static struct hda_verb alc269_init_verbs[] = {
/* 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(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
@@ -10947,7 +12179,7 @@ static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
static int alc269_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
+ int i, err;
static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
@@ -10970,14 +12202,28 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ /* create a beep mixer control if the pin 0x1d isn't assigned */
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
+ if (spec->autocfg.input_pins[i] == 0x1d)
+ break;
+ if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
+ spec->mixers[spec->num_mixers++] = alc269_beep_mixer;
+
spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
+ /* set default input source */
+ snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0],
+ 0, AC_VERB_SET_CONNECT_SEL,
+ spec->input_mux->items[0].index);
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
+ spec->mixers[spec->num_mixers] = alc269_capture_mixer;
+ spec->num_mixers++;
+
return 1;
}
@@ -10994,23 +12240,33 @@ static void alc269_auto_init(struct hda_codec *codec)
alc269_auto_init_hp_out(codec);
alc269_auto_init_analog_input(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
/*
* configuration and preset
*/
static const char *alc269_models[ALC269_MODEL_LAST] = {
- [ALC269_BASIC] = "basic",
+ [ALC269_BASIC] = "basic",
+ [ALC269_QUANTA_FL1] = "quanta",
+ [ALC269_ASUS_EEEPC_P703] = "eeepc-p703",
+ [ALC269_ASUS_EEEPC_P901] = "eeepc-p901"
};
static struct snd_pci_quirk alc269_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
+ SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
+ ALC269_ASUS_EEEPC_P703),
+ SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
+ ALC269_ASUS_EEEPC_P901),
+ SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
+ ALC269_ASUS_EEEPC_P901),
{}
};
static struct alc_config_preset alc269_presets[] = {
[ALC269_BASIC] = {
- .mixers = { alc269_base_mixer },
+ .mixers = { alc269_base_mixer, alc269_capture_mixer },
.init_verbs = { alc269_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
.dac_nids = alc269_dac_nids,
@@ -11019,6 +12275,44 @@ static struct alc_config_preset alc269_presets[] = {
.channel_mode = alc269_modes,
.input_mux = &alc269_capture_source,
},
+ [ALC269_QUANTA_FL1] = {
+ .mixers = { alc269_quanta_fl1_mixer },
+ .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_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,
+ .unsol_event = alc269_quanta_fl1_unsol_event,
+ .init_hook = alc269_quanta_fl1_init_hook,
+ },
+ [ALC269_ASUS_EEEPC_P703] = {
+ .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
+ .init_verbs = { alc269_init_verbs,
+ alc269_eeepc_amic_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_eeepc_amic_capture_source,
+ .unsol_event = alc269_eeepc_amic_unsol_event,
+ .init_hook = alc269_eeepc_amic_inithook,
+ },
+ [ALC269_ASUS_EEEPC_P901] = {
+ .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer},
+ .init_verbs = { alc269_init_verbs,
+ alc269_eeepc_dmic_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_eeepc_dmic_capture_source,
+ .unsol_event = alc269_eeepc_dmic_unsol_event,
+ .init_hook = alc269_eeepc_dmic_inithook,
+ },
};
static int patch_alc269(struct hda_codec *codec)
@@ -11033,6 +12327,8 @@ static int patch_alc269(struct hda_codec *codec)
codec->spec = spec;
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
alc269_models,
alc269_cfg_tbl);
@@ -11070,8 +12366,7 @@ static int patch_alc269(struct hda_codec *codec)
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++;
+ spec->capsrc_nids = alc269_capsrc_nids;
codec->patch_ops = alc_patch_ops;
if (board_config == ALC269_AUTO)
@@ -11273,7 +12568,7 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
-
+
/*Capture mixer control */
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
@@ -11416,20 +12711,20 @@ static struct hda_verb alc861_base_init_verbs[] = {
/* route front mic to ADC1*/
{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
+
/* Unmute DAC0~3 & spdif out*/
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
+
/* Unmute Mixer 14 (mic) 1c (Line in)*/
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
+
/* Unmute Stereo Mixer 15 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -11485,13 +12780,13 @@ static struct hda_verb alc861_threestack_init_verbs[] = {
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
+
/* Unmute Mixer 14 (mic) 1c (Line in)*/
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
+
/* Unmute Stereo Mixer 15 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -11547,13 +12842,13 @@ static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
+
/* Unmute Mixer 14 (mic) 1c (Line in)*/
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
+
/* Unmute Stereo Mixer 15 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -11618,7 +12913,7 @@ static struct hda_verb alc861_asus_init_verbs[] = {
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
+
/* Unmute Stereo Mixer 15 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -11655,20 +12950,20 @@ static struct hda_verb alc861_auto_init_verbs[] = {
*/
/* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
+
/* Unmute DAC0~3 & spdif out*/
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
+
/* Unmute Mixer 14 (mic) 1c (Line in)*/
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
+
/* Unmute Stereo Mixer 15 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -12026,7 +13321,7 @@ static void alc861_auto_init(struct hda_codec *codec)
alc861_auto_init_hp_out(codec);
alc861_auto_init_analog_input(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -12243,7 +13538,7 @@ static int patch_alc861(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc861_loopbacks;
#endif
-
+
return 0;
}
@@ -12497,7 +13792,7 @@ static struct snd_kcontrol_new alc861vd_hp_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
+
{ } /* end */
};
@@ -12631,12 +13926,18 @@ static struct hda_verb alc861vd_eapd_verbs[] = {
{ }
};
+static struct hda_verb alc660vd_eapd_verbs[] = {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ { }
+};
+
static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
{}
};
@@ -12698,7 +13999,7 @@ static struct hda_verb alc861vd_dallas_verbs[] = {
{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
+
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
@@ -12723,7 +14024,7 @@ static struct hda_verb alc861vd_dallas_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
{ } /* end */
@@ -12776,6 +14077,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
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, 0x1633, "Asus V1Sn", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
@@ -12786,6 +14088,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
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(0x17aa, 0x384e, "Lenovo 3000 N200", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
{}
};
@@ -12880,7 +14183,7 @@ static struct alc_config_preset alc861vd_presets[] = {
.input_mux = &alc861vd_hp_capture_source,
.unsol_event = alc861vd_dallas_unsol_event,
.init_hook = alc861vd_dallas_automute,
- },
+ },
};
/*
@@ -12944,6 +14247,8 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
}
}
+#define alc861vd_auto_init_input_src alc882_auto_init_input_src
+
#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02)
#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
@@ -13126,8 +14431,9 @@ static void alc861vd_auto_init(struct hda_codec *codec)
alc861vd_auto_init_multi_out(codec);
alc861vd_auto_init_hp_out(codec);
alc861vd_auto_init_analog_input(codec);
+ alc861vd_auto_init_input_src(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
static int patch_alc861vd(struct hda_codec *codec)
@@ -13168,11 +14474,19 @@ static int patch_alc861vd(struct hda_codec *codec)
if (board_config != ALC861VD_AUTO)
setup_preset(spec, &alc861vd_presets[board_config]);
- spec->stream_name_analog = "ALC861VD Analog";
+ if (codec->vendor_id == 0x10ec0660) {
+ spec->stream_name_analog = "ALC660-VD Analog";
+ spec->stream_name_digital = "ALC660-VD Digital";
+ /* always turn on EAPD */
+ spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+ } else {
+ spec->stream_name_analog = "ALC861VD Analog";
+ spec->stream_name_digital = "ALC861VD Digital";
+ }
+
spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
- spec->stream_name_digital = "ALC861VD Digital";
spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
@@ -13251,6 +14565,23 @@ static struct hda_input_mux alc662_eeepc_capture_source = {
},
};
+static struct hda_input_mux alc663_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc663_m51va_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Ext-Mic", 0x0 },
+ { "D-Mic", 0x9 },
+ },
+};
+
#define alc662_mux_enum_info alc_mux_enum_info
#define alc662_mux_enum_get alc_mux_enum_get
#define alc662_mux_enum_put alc882_mux_enum_put
@@ -13431,6 +14762,151 @@ static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
{ } /* end */
};
+static struct hda_bind_ctls alc663_asus_bind_master_vol = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls alc663_asus_one_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc663_m51va_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_bind_ctls alc663_asus_tree_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+
+ { } /* end */
+};
+
+static struct hda_bind_ctls alc663_asus_four_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc662_1bjd_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 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("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_bind_ctls alc663_asus_two_bind_master_vol = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls alc663_asus_two_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume",
+ &alc663_asus_two_bind_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ 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 alc663_asus_15jd_clfe_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 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),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc663_g71v_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 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("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc663_g50v_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 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("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc662_chmode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -13501,6 +14977,11 @@ static struct hda_verb alc662_init_verbs[] = {
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /* always trun on EAPD */
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+
{ }
};
@@ -13571,6 +15052,125 @@ static struct hda_verb alc662_auto_init_verbs[] = {
{ }
};
+/* additional verbs for ALC663 */
+static struct hda_verb alc663_auto_init_verbs[] = {
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ { }
+};
+
+static struct hda_verb alc663_m51va_init_verbs[] = {
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_21jd_amic_init_verbs[] = {
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc662_1bjd_amic_init_verbs[] = {
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_15jd_amic_init_verbs[] = {
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = {
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = {
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_g71v_init_verbs[] = {
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
+ /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */
+
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
+
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_g50v_init_verbs[] = {
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
+
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc662_ecs_init_verbs[] = {
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
/* capture mixer elements */
static struct snd_kcontrol_new alc662_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
@@ -13590,6 +15190,12 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
{
unsigned int present;
@@ -13670,12 +15276,12 @@ static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
if (present) {
/* mute internal speaker */
snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
+ 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);
+ HDA_AMP_MUTE, mute);
}
}
@@ -13692,6 +15298,377 @@ static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
alc662_eeepc_ep20_automute(codec);
}
+static void alc663_m51va_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+}
+
+static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+}
+
+static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+}
+
+static void alc662_f5z_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? 0 : PIN_OUT;
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
+}
+
+static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present1, present2;
+
+ present1 = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ present2 = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+
+ if (present1 || present2) {
+ snd_hda_codec_write_cache(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ } else {
+ snd_hda_codec_write_cache(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ }
+}
+
+static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present1, present2;
+
+ present1 = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ present2 = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+
+ if (present1 || present2) {
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), AMP_IN_MUTE(0));
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), AMP_IN_MUTE(0));
+ } else {
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), 0);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), 0);
+ }
+}
+
+static void alc663_m51va_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+ snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+ snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
+ snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ 0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
+}
+
+static void alc663_m51va_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_m51va_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc663_m51va_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_m51va_inithook(struct hda_codec *codec)
+{
+ alc663_m51va_speaker_automute(codec);
+ alc663_m51va_mic_automute(codec);
+}
+
+/* ***************** Mode1 ******************************/
+static void alc663_mode1_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_m51va_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_mode1_inithook(struct hda_codec *codec)
+{
+ alc663_m51va_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode2 ******************************/
+static void alc662_mode2_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc662_f5z_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc662_mode2_inithook(struct hda_codec *codec)
+{
+ alc662_f5z_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode3 ******************************/
+static void alc663_mode3_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_two_hp_m1_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_mode3_inithook(struct hda_codec *codec)
+{
+ alc663_two_hp_m1_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode4 ******************************/
+static void alc663_mode4_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_21jd_two_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_mode4_inithook(struct hda_codec *codec)
+{
+ alc663_21jd_two_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode5 ******************************/
+static void alc663_mode5_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_15jd_two_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_mode5_inithook(struct hda_codec *codec)
+{
+ alc663_15jd_two_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode6 ******************************/
+static void alc663_mode6_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_two_hp_m2_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_mode6_inithook(struct hda_codec *codec)
+{
+ alc663_two_hp_m2_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+
+static void alc663_g71v_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc663_g71v_front_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc663_g71v_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_g71v_hp_automute(codec);
+ break;
+ case ALC880_FRONT_EVENT:
+ alc663_g71v_front_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_g71v_inithook(struct hda_codec *codec)
+{
+ alc663_g71v_front_automute(codec);
+ alc663_g71v_hp_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+
+static void alc663_g50v_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_m51va_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc662_eeepc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc663_g50v_inithook(struct hda_codec *codec)
+{
+ alc663_m51va_speaker_automute(codec);
+ alc662_eeepc_mic_automute(codec);
+}
+
+/* bind hp and internal speaker mute (with plug check) */
+static int alc662_ecs_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change;
+
+ change = snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp[0] ? 0 : HDA_AMP_MUTE);
+ change |= snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp[1] ? 0 : HDA_AMP_MUTE);
+ if (change)
+ alc262_hippo1_automute(codec);
+ return change;
+}
+
+static struct snd_kcontrol_new alc662_ecs_mixer[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc662_ecs_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+ },
+
+ HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("e-Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("i-Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc662_loopbacks alc880_loopbacks
#endif
@@ -13714,14 +15691,71 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
[ALC662_LENOVO_101E] = "lenovo-101e",
[ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
[ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
+ [ALC662_ECS] = "ecs",
+ [ALC663_ASUS_M51VA] = "m51va",
+ [ALC663_ASUS_G71V] = "g71v",
+ [ALC663_ASUS_H13] = "h13",
+ [ALC663_ASUS_G50V] = "g50v",
+ [ALC663_ASUS_MODE1] = "asus-mode1",
+ [ALC662_ASUS_MODE2] = "asus-mode2",
+ [ALC663_ASUS_MODE3] = "asus-mode3",
+ [ALC663_ASUS_MODE4] = "asus-mode4",
+ [ALC663_ASUS_MODE5] = "asus-mode5",
+ [ALC663_ASUS_MODE6] = "asus-mode6",
[ALC662_AUTO] = "auto",
};
static struct snd_pci_quirk alc662_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
+ SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V),
SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
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(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
+ SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
+ SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
+ ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
+ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
+ SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
+ ALC662_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0",
+ ALC662_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13),
+ SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13),
+ SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13),
{}
};
@@ -13809,7 +15843,150 @@ static struct alc_config_preset alc662_presets[] = {
.unsol_event = alc662_eeepc_ep20_unsol_event,
.init_hook = alc662_eeepc_ep20_inithook,
},
-
+ [ALC662_ECS] = {
+ .mixers = { alc662_ecs_mixer, alc662_capture_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc662_ecs_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc662_eeepc_unsol_event,
+ .init_hook = alc662_eeepc_inithook,
+ },
+ [ALC663_ASUS_M51VA] = {
+ .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+ .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc663_m51va_capture_source,
+ .unsol_event = alc663_m51va_unsol_event,
+ .init_hook = alc663_m51va_inithook,
+ },
+ [ALC663_ASUS_G71V] = {
+ .mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+ .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc663_g71v_unsol_event,
+ .init_hook = alc663_g71v_inithook,
+ },
+ [ALC663_ASUS_H13] = {
+ .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+ .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc663_m51va_capture_source,
+ .unsol_event = alc663_m51va_unsol_event,
+ .init_hook = alc663_m51va_inithook,
+ },
+ [ALC663_ASUS_G50V] = {
+ .mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+ .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
+ .channel_mode = alc662_3ST_6ch_modes,
+ .input_mux = &alc663_capture_source,
+ .unsol_event = alc663_g50v_unsol_event,
+ .init_hook = alc663_g50v_inithook,
+ },
+ [ALC663_ASUS_MODE1] = {
+ .mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc663_21jd_amic_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc663_mode1_unsol_event,
+ .init_hook = alc663_mode1_inithook,
+ },
+ [ALC662_ASUS_MODE2] = {
+ .mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc662_1bjd_amic_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc662_mode2_unsol_event,
+ .init_hook = alc662_mode2_inithook,
+ },
+ [ALC663_ASUS_MODE3] = {
+ .mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc663_two_hp_amic_m1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc663_mode3_unsol_event,
+ .init_hook = alc663_mode3_inithook,
+ },
+ [ALC663_ASUS_MODE4] = {
+ .mixers = { alc663_asus_21jd_clfe_mixer,
+ alc662_auto_capture_mixer},
+ .init_verbs = { alc662_init_verbs,
+ alc663_21jd_amic_init_verbs},
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc663_mode4_unsol_event,
+ .init_hook = alc663_mode4_inithook,
+ },
+ [ALC663_ASUS_MODE5] = {
+ .mixers = { alc663_asus_15jd_clfe_mixer,
+ alc662_auto_capture_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc663_15jd_amic_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc663_mode5_unsol_event,
+ .init_hook = alc663_mode5_inithook,
+ },
+ [ALC663_ASUS_MODE6] = {
+ .mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc663_two_hp_amic_m2_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .input_mux = &alc662_eeepc_capture_source,
+ .unsol_event = alc663_mode6_unsol_event,
+ .init_hook = alc663_mode6_inithook,
+ },
};
@@ -13846,15 +16023,15 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
"Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2,
+ HDA_COMPOSE_AMP_VAL(0x0e, 1, 0,
HDA_INPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
"LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2,
+ HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
HDA_INPUT));
if (err < 0)
return err;
@@ -13866,9 +16043,9 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2,
- HDA_INPUT));
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
+ 3, 0, HDA_INPUT));
if (err < 0)
return err;
}
@@ -13887,6 +16064,14 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
if (!pin)
return 0;
+ if (pin == 0x17) {
+ /* ALC663 has a mono output pin on 0x17 */
+ sprintf(name, "%s Playback Switch", pfx);
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(pin, 2, 0, HDA_OUTPUT));
+ return err;
+ }
+
if (alc880_is_fixed_pin(pin)) {
nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
/* printk("DAC nid=%x\n",nid); */
@@ -14011,6 +16196,8 @@ static void alc662_auto_init_analog_input(struct hda_codec *codec)
}
}
+#define alc662_auto_init_input_src alc882_auto_init_input_src
+
static int alc662_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -14053,8 +16240,16 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
-
+
spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
+ if (codec->vendor_id == 0x10ec0663)
+ spec->init_verbs[spec->num_init_verbs++] =
+ alc663_auto_init_verbs;
+
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
+
spec->mixers[spec->num_mixers] = alc662_capture_mixer;
spec->num_mixers++;
return 1;
@@ -14067,8 +16262,9 @@ static void alc662_auto_init(struct hda_codec *codec)
alc662_auto_init_multi_out(codec);
alc662_auto_init_hp_out(codec);
alc662_auto_init_analog_input(codec);
+ alc662_auto_init_input_src(codec);
if (spec->unsol_event)
- alc_sku_automute(codec);
+ alc_inithook(codec);
}
static int patch_alc662(struct hda_codec *codec)
@@ -14082,6 +16278,8 @@ static int patch_alc662(struct hda_codec *codec)
codec->spec = spec;
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
alc662_models,
alc662_cfg_tbl);
@@ -14108,11 +16306,20 @@ static int patch_alc662(struct hda_codec *codec)
if (board_config != ALC662_AUTO)
setup_preset(spec, &alc662_presets[board_config]);
- spec->stream_name_analog = "ALC662 Analog";
+ if (codec->vendor_id == 0x10ec0663) {
+ spec->stream_name_analog = "ALC663 Analog";
+ spec->stream_name_digital = "ALC663 Digital";
+ } else if (codec->vendor_id == 0x10ec0272) {
+ spec->stream_name_analog = "ALC272 Analog";
+ spec->stream_name_digital = "ALC272 Digital";
+ } else {
+ spec->stream_name_analog = "ALC662 Analog";
+ spec->stream_name_digital = "ALC662 Digital";
+ }
+
spec->stream_analog_playback = &alc662_pcm_analog_playback;
spec->stream_analog_capture = &alc662_pcm_analog_capture;
- spec->stream_name_digital = "ALC662 Digital";
spec->stream_digital_playback = &alc662_pcm_digital_playback;
spec->stream_digital_capture = &alc662_pcm_digital_capture;
@@ -14142,6 +16349,7 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
{ .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
+ { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
@@ -14151,13 +16359,19 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
.patch = patch_alc883 },
{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
.patch = patch_alc662 },
+ { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+ { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
+ .patch = patch_alc882 }, /* should be patch_alc883() in future */
{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
.patch = patch_alc882 }, /* should be patch_alc883() in future */
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
+ { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 },
{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+ { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
+ .patch = patch_alc883 },
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index a4f44a00bae..a2ac7205d45 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -33,10 +33,12 @@
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_patch.h"
+#include "hda_beep.h"
#define NUM_CONTROL_ALLOC 32
#define STAC_PWR_EVENT 0x20
#define STAC_HP_EVENT 0x30
+#define STAC_VREF_EVENT 0x40
enum {
STAC_REF,
@@ -71,9 +73,15 @@ enum {
};
enum {
+ STAC_92HD83XXX_REF,
+ STAC_92HD83XXX_MODELS
+};
+
+enum {
STAC_92HD71BXX_REF,
STAC_DELL_M4_1,
STAC_DELL_M4_2,
+ STAC_HP_M4,
STAC_92HD71BXX_MODELS
};
@@ -94,6 +102,9 @@ enum {
STAC_INTEL_MAC_V3,
STAC_INTEL_MAC_V4,
STAC_INTEL_MAC_V5,
+ STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter
+ * is given, one of the above models will be
+ * chosen according to the subsystem id. */
/* for backward compatibility */
STAC_MACMINI,
STAC_MACBOOK,
@@ -101,6 +112,7 @@ enum {
STAC_MACBOOK_PRO_V2,
STAC_IMAC_INTEL,
STAC_IMAC_INTEL_20,
+ STAC_ECS_202,
STAC_922X_DELL_D81,
STAC_922X_DELL_D82,
STAC_922X_DELL_M81,
@@ -127,6 +139,7 @@ struct sigmatel_spec {
unsigned int mic_switch: 1;
unsigned int alt_switch: 1;
unsigned int hp_detect: 1;
+ unsigned int spdif_mute: 1;
/* gpio lines */
unsigned int eapd_mask;
@@ -135,17 +148,22 @@ struct sigmatel_spec {
unsigned int gpio_data;
unsigned int gpio_mute;
+ /* stream */
+ unsigned int stream_delay;
+
/* analog loopback */
unsigned char aloopback_mask;
unsigned char aloopback_shift;
/* power management */
unsigned int num_pwrs;
+ unsigned int *pwr_mapping;
hda_nid_t *pwr_nids;
hda_nid_t *dac_list;
/* playback */
struct hda_input_mux *mono_mux;
+ struct hda_input_mux *amp_mux;
unsigned int cur_mmux;
struct hda_multi_out multiout;
hda_nid_t dac_nids[5];
@@ -159,8 +177,14 @@ struct sigmatel_spec {
unsigned int num_dmics;
hda_nid_t *dmux_nids;
unsigned int num_dmuxes;
+ hda_nid_t *smux_nids;
+ unsigned int num_smuxes;
+ const char **spdif_labels;
+
hda_nid_t dig_in_nid;
hda_nid_t mono_nid;
+ hda_nid_t anabeep_nid;
+ hda_nid_t digbeep_nid;
/* pin widgets */
hda_nid_t *pin_nids;
@@ -177,6 +201,12 @@ struct sigmatel_spec {
unsigned int cur_dmux[2];
struct hda_input_mux *input_mux;
unsigned int cur_mux[3];
+ struct hda_input_mux *sinput_mux;
+ unsigned int cur_smux[2];
+ unsigned int cur_amux;
+ hda_nid_t *amp_nids;
+ unsigned int num_amps;
+ unsigned int powerdown_adcs;
/* i/o switches */
unsigned int io_switch[2];
@@ -192,6 +222,8 @@ 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_smux;
+ struct hda_input_mux private_amp_mux;
struct hda_input_mux private_mono_mux;
};
@@ -212,10 +244,19 @@ static hda_nid_t stac92hd73xx_pwr_nids[8] = {
0x0f, 0x10, 0x11
};
+static hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
+ 0x26, 0,
+};
+
static hda_nid_t stac92hd73xx_adc_nids[2] = {
0x1a, 0x1b
};
+#define DELL_M6_AMP 2
+static hda_nid_t stac92hd73xx_amp_nids[3] = {
+ 0x0b, 0x0c, 0x0e
+};
+
#define STAC92HD73XX_NUM_DMICS 2
static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
0x13, 0x14, 0
@@ -234,6 +275,41 @@ static hda_nid_t stac92hd73xx_dmux_nids[2] = {
0x20, 0x21,
};
+static hda_nid_t stac92hd73xx_smux_nids[2] = {
+ 0x22, 0x23,
+};
+
+#define STAC92HD83XXX_NUM_DMICS 2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+ 0x11, 0x12, 0
+};
+
+#define STAC92HD81_DAC_COUNT 2
+#define STAC92HD83_DAC_COUNT 3
+static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = {
+ 0x13, 0x14, 0x22,
+};
+
+static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
+ 0x17, 0x18,
+};
+
+static hda_nid_t stac92hd83xxx_adc_nids[2] = {
+ 0x15, 0x16,
+};
+
+static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
+ 0xa, 0xb, 0xd, 0xe,
+};
+
+static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
+ 0x1e, 0,
+};
+
+static unsigned int stac92hd83xxx_pwr_mapping[4] = {
+ 0x03, 0x0c, 0x10, 0x40,
+};
+
static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
0x0a, 0x0d, 0x0f
};
@@ -246,8 +322,12 @@ static hda_nid_t stac92hd71bxx_mux_nids[2] = {
0x1a, 0x1b
};
-static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
- 0x1c,
+static hda_nid_t stac92hd71bxx_dmux_nids[2] = {
+ 0x1c, 0x1d,
+};
+
+static hda_nid_t stac92hd71bxx_smux_nids[2] = {
+ 0x24, 0x25,
};
static hda_nid_t stac92hd71bxx_dac_nids[1] = {
@@ -259,6 +339,10 @@ static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
0x18, 0x19, 0
};
+static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
+ 0x22, 0
+};
+
static hda_nid_t stac925x_adc_nids[1] = {
0x03,
};
@@ -296,6 +380,10 @@ static hda_nid_t stac927x_mux_nids[3] = {
0x15, 0x16, 0x17
};
+static hda_nid_t stac927x_smux_nids[1] = {
+ 0x21,
+};
+
static hda_nid_t stac927x_dac_nids[6] = {
0x02, 0x03, 0x04, 0x05, 0x06, 0
};
@@ -309,6 +397,11 @@ static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
0x13, 0x14, 0
};
+static const char *stac927x_spdif_labels[5] = {
+ "Digital Playback", "ADAT", "Analog Mux 1",
+ "Analog Mux 2", "Analog Mux 3"
+};
+
static hda_nid_t stac9205_adc_nids[2] = {
0x12, 0x13
};
@@ -321,6 +414,10 @@ static hda_nid_t stac9205_dmux_nids[1] = {
0x1d,
};
+static hda_nid_t stac9205_smux_nids[1] = {
+ 0x21,
+};
+
#define STAC9205_NUM_DMICS 2
static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
0x17, 0x18, 0
@@ -344,12 +441,18 @@ static hda_nid_t stac922x_pin_nids[10] = {
static hda_nid_t stac92hd73xx_pin_nids[13] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x1e, 0x22
+ 0x14, 0x22, 0x23
};
-static hda_nid_t stac92hd71bxx_pin_nids[10] = {
+static hda_nid_t stac92hd83xxx_pin_nids[14] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x1d, 0x1e, 0x1f, 0x20
+};
+static hda_nid_t stac92hd71bxx_pin_nids[11] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x14, 0x18, 0x19, 0x1e,
+ 0x1f,
};
static hda_nid_t stac927x_pin_nids[14] = {
@@ -364,6 +467,34 @@ static hda_nid_t stac9205_pin_nids[12] = {
0x21, 0x22,
};
+#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info
+
+static int stac92xx_amp_volume_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;
+ hda_nid_t nid = spec->amp_nids[spec->cur_amux];
+
+ kcontrol->private_value ^= get_amp_nid(kcontrol);
+ kcontrol->private_value |= nid;
+
+ return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
+}
+
+static int stac92xx_amp_volume_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;
+ hda_nid_t nid = spec->amp_nids[spec->cur_amux];
+
+ kcontrol->private_value ^= get_amp_nid(kcontrol);
+ kcontrol->private_value |= nid;
+
+ return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+}
+
static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -394,6 +525,58 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
}
+static int stac92xx_smux_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->sinput_mux, uinfo);
+}
+
+static int stac92xx_smux_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;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
+ return 0;
+}
+
+static int stac92xx_smux_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;
+ struct hda_input_mux *smux = &spec->private_smux;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int err, val;
+ hda_nid_t nid;
+
+ err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol,
+ spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]);
+ if (err < 0)
+ return err;
+
+ if (spec->spdif_mute) {
+ if (smux_idx == 0)
+ nid = spec->multiout.dig_out_nid;
+ else
+ nid = codec->slave_dig_outs[smux_idx - 1];
+ if (spec->cur_smux[smux_idx] == smux->num_items - 1)
+ val = AMP_OUT_MUTE;
+ if (smux_idx == 0)
+ nid = spec->multiout.dig_out_nid;
+ else
+ nid = codec->slave_dig_outs[smux_idx - 1];
+ /* un/mute SPDIF out */
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+ return 0;
+}
+
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -449,6 +632,41 @@ static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
spec->mono_nid, &spec->cur_mmux);
}
+static int stac92xx_amp_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->amp_mux, uinfo);
+}
+
+static int stac92xx_amp_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_amux;
+ return 0;
+}
+
+static int stac92xx_amp_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;
+ struct snd_kcontrol *ctl =
+ snd_hda_find_mixer_ctl(codec, "Amp Capture Volume");
+ if (!ctl)
+ return -EINVAL;
+
+ snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+
+ return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol,
+ 0, &spec->cur_amux);
+}
+
#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
@@ -543,8 +761,8 @@ static struct hda_verb dell_eq_core_init[] = {
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
/* setup audio connections */
{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02},
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},
/* setup adcs to point to mixer */
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -557,8 +775,9 @@ static struct hda_verb dell_eq_core_init[] = {
};
static struct hda_verb dell_m6_core_init[] = {
- /* set master volume and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* set master volume to max value without distortion
+ * and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
/* setup audio connections */
{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
@@ -624,33 +843,47 @@ static struct hda_verb stac92hd73xx_10ch_core_init[] = {
{}
};
+static struct hda_verb stac92hd83xxx_core_init[] = {
+ /* start of config #1 */
+ { 0xe, AC_VERB_SET_CONNECT_SEL, 0x3},
+
+ /* start of config #2 */
+ { 0xa, AC_VERB_SET_CONNECT_SEL, 0x0},
+ { 0xb, AC_VERB_SET_CONNECT_SEL, 0x0},
+ { 0xd, AC_VERB_SET_CONNECT_SEL, 0x1},
+
+ /* power state controls amps */
+ { 0x01, AC_VERB_SET_EAPD, 1 << 2},
+};
+
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)},
};
+#define HD_DISABLE_PORTF 2
static struct hda_verb stac92hd71bxx_analog_core_init[] = {
+ /* start of config #1 */
+
+ /* connect port 0f to audio mixer */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+ /* unmute right and left channels for node 0x0f */
+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ /* start of config #2 */
+
/* 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 */
+ /* unmute right and left channels for nodes 0x0a, 0xd */
{ 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)},
{}
};
@@ -679,12 +912,16 @@ static struct hda_verb d965_core_init[] = {
static struct hda_verb stac927x_core_init[] = {
/* set master volume and direct control */
{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* enable analog pc beep path */
+ { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
{}
};
static struct hda_verb stac9205_core_init[] = {
/* set master volume and direct control */
{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* enable analog pc beep path */
+ { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
{}
};
@@ -698,6 +935,31 @@ static struct hda_verb stac9205_core_init[] = {
.put = stac92xx_mono_mux_enum_put, \
}
+#define STAC_AMP_MUX \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Amp Selector Capture Switch", \
+ .count = 1, \
+ .info = stac92xx_amp_mux_enum_info, \
+ .get = stac92xx_amp_mux_enum_get, \
+ .put = stac92xx_amp_mux_enum_put, \
+ }
+
+#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = stac92xx_amp_volume_info, \
+ .get = stac92xx_amp_volume_get, \
+ .put = stac92xx_amp_volume_put, \
+ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
+ }
+
#define STAC_INPUT_SOURCE(cnt) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -725,33 +987,36 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
STAC_INPUT_SOURCE(1),
HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
{ } /* end */
};
+#define DELL_M6_MIXER 6
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),
-
+ /* start of config #1 */
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("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+
+ /* start of config #2 */
+ 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("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),
+ 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),
+
{ } /* end */
};
@@ -807,19 +1072,59 @@ static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
{ } /* end */
};
+
+static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT),
+
+ /*
+ HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
+ */
+ { } /* end */
+};
+
static struct snd_kcontrol_new stac92hd71bxx_analog_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),
+ /* analog pc-beep replaced with digital beep support */
+ /*
+ HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
+ */
+
+ HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT),
+
+ HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT),
+
+ HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT),
{ } /* end */
};
@@ -829,11 +1134,9 @@ static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
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 */
};
@@ -841,7 +1144,6 @@ static struct snd_kcontrol_new stac925x_mixer[] = {
STAC_INPUT_SOURCE(1),
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
{ } /* end */
};
@@ -851,12 +1153,9 @@ static struct snd_kcontrol_new stac9205_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT),
-
{ } /* end */
};
@@ -865,11 +1164,9 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
STAC_INPUT_SOURCE(2),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT),
{ } /* end */
};
@@ -880,15 +1177,12 @@ static struct snd_kcontrol_new stac927x_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT),
{ } /* end */
};
@@ -901,6 +1195,15 @@ static struct snd_kcontrol_new stac_dmux_mixer = {
.put = stac92xx_dmux_enum_put,
};
+static struct snd_kcontrol_new stac_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ /* count set later */
+ .info = stac92xx_smux_enum_info,
+ .get = stac92xx_smux_enum_get,
+ .put = stac92xx_smux_enum_put,
+};
+
static const char *slave_vols[] = {
"Front Playback Volume",
"Surround Playback Volume",
@@ -952,6 +1255,22 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+ if (spec->num_smuxes > 0) {
+ int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid);
+ struct hda_input_mux *smux = &spec->private_smux;
+ /* check for mute support on SPDIF out */
+ if (wcaps & AC_WCAP_OUT_AMP) {
+ smux->items[smux->num_items].label = "Off";
+ smux->items[smux->num_items].index = 0;
+ smux->num_items++;
+ spec->spdif_mute = 1;
+ }
+ stac_smux_mixer.count = spec->num_smuxes;
+ err = snd_ctl_add(codec->bus->card,
+ snd_ctl_new1(&stac_smux_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);
@@ -963,7 +1282,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return err;
spec->multiout.share_spdif = 1;
}
- if (spec->dig_in_nid) {
+ if (spec->dig_in_nid && (!spec->gpio_dir & 0x01)) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
if (err < 0)
return err;
@@ -1311,40 +1630,65 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref92hd71bxx_pin_configs[10] = {
+static unsigned int ref92hd83xxx_pin_configs[14] = {
+ 0x02214030, 0x02211010, 0x02a19020, 0x02170130,
+ 0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
+ 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0,
+ 0x01451160, 0x98560170,
+};
+
+static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
+ [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
+};
+
+static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
+ [STAC_92HD83XXX_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+};
+
+static unsigned int ref92hd71bxx_pin_configs[11] = {
0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
- 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
- 0x90a000f0, 0x01452050,
+ 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
+ 0x90a000f0, 0x01452050, 0x01452050,
};
-static unsigned int dell_m4_1_pin_configs[13] = {
+static unsigned int dell_m4_1_pin_configs[11] = {
0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x4f0000f0,
+ 0x40f000f0, 0x4f0000f0, 0x4f0000f0,
};
-static unsigned int dell_m4_2_pin_configs[13] = {
+static unsigned int dell_m4_2_pin_configs[11] = {
0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
- 0x40f000f0, 0x044413b0,
+ 0x40f000f0, 0x044413b0, 0x044413b0,
};
static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
[STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
[STAC_DELL_M4_1] = dell_m4_1_pin_configs,
[STAC_DELL_M4_2] = dell_m4_2_pin_configs,
+ [STAC_HP_M4] = NULL,
};
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
[STAC_92HD71BXX_REF] = "ref",
[STAC_DELL_M4_1] = "dell-m4-1",
[STAC_DELL_M4_2] = "dell-m4-2",
+ [STAC_HP_M4] = "hp-m4",
};
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
+ "unknown HP", STAC_HP_M4),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
"unknown Dell", STAC_DELL_M4_1),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -1463,6 +1807,11 @@ static unsigned int intel_mac_v5_pin_configs[10] = {
0x400000fc, 0x400000fb,
};
+static unsigned int ecs202_pin_configs[10] = {
+ 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
+ 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
+ 0x9037012e, 0x40e000f2,
+};
static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
[STAC_D945_REF] = ref922x_pin_configs,
@@ -1473,6 +1822,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
[STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
[STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
[STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
+ [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs,
/* for backward compatibility */
[STAC_MACMINI] = intel_mac_v3_pin_configs,
[STAC_MACBOOK] = intel_mac_v5_pin_configs,
@@ -1480,6 +1830,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
[STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
[STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
[STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
+ [STAC_ECS_202] = ecs202_pin_configs,
[STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
[STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
[STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
@@ -1495,6 +1846,7 @@ static const char *stac922x_models[STAC_922X_MODELS] = {
[STAC_INTEL_MAC_V3] = "intel-mac-v3",
[STAC_INTEL_MAC_V4] = "intel-mac-v4",
[STAC_INTEL_MAC_V5] = "intel-mac-v5",
+ [STAC_INTEL_MAC_AUTO] = "intel-mac-auto",
/* for backward compatibility */
[STAC_MACMINI] = "macmini",
[STAC_MACBOOK] = "macbook",
@@ -1502,6 +1854,7 @@ static const char *stac922x_models[STAC_922X_MODELS] = {
[STAC_MACBOOK_PRO_V2] = "macbook-pro",
[STAC_IMAC_INTEL] = "imac-intel",
[STAC_IMAC_INTEL_20] = "imac-intel-20",
+ [STAC_ECS_202] = "ecs202",
[STAC_922X_DELL_D81] = "dell-d81",
[STAC_922X_DELL_D82] = "dell-d82",
[STAC_922X_DELL_M81] = "dell-m81",
@@ -1566,9 +1919,9 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
"Intel D945P", STAC_D945GTP5),
/* other systems */
- /* Apple Mac Mini (early 2006) */
+ /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
SND_PCI_QUIRK(0x8384, 0x7680,
- "Mac Mini", STAC_INTEL_MAC_V3),
+ "Mac", STAC_INTEL_MAC_AUTO),
/* Dell systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
"unknown Dell", STAC_922X_DELL_D81),
@@ -1588,6 +1941,33 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
"unknown Dell", STAC_922X_DELL_D81),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
"Dell XPS M1210", STAC_922X_DELL_M82),
+ /* ECS/PC Chips boards */
+ SND_PCI_QUIRK(0x1019, 0x2144,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2608,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2633,
+ "ECS/PC chips P17G/1333", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2811,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2812,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2813,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2814,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2815,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2816,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2817,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2818,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2819,
+ "ECS/PC chips", STAC_ECS_202),
+ SND_PCI_QUIRK(0x1019, 0x2820,
+ "ECS/PC chips", STAC_ECS_202),
{} /* terminator */
};
@@ -1667,8 +2047,8 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
/* 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, 0x022f, "Dell Inspiron 1525", STAC_DELL_3ST),
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),
@@ -1754,12 +2134,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
"unknown Dell", STAC_9205_DELL_M42),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
"Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
- "Dell Precision", STAC_9205_DELL_M43),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
"Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
- "Dell Precision", STAC_9205_DELL_M43),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
"Dell Precision", STAC_9205_DELL_M43),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
@@ -1770,18 +2146,14 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
"Dell Precision", STAC_9205_DELL_M43),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
"Dell Precision M4300", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
- "Dell Inspiron", STAC_9205_DELL_M44),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
- "Dell Inspiron", STAC_9205_DELL_M44),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
- "Dell Inspiron", STAC_9205_DELL_M44),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
- "Dell Inspiron", STAC_9205_DELL_M44),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
"unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
+ "Dell Precision", STAC_9205_DELL_M43),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
"Dell Inspiron", STAC_9205_DELL_M44),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
@@ -1859,6 +2231,8 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
+ if (spec->stream_delay)
+ msleep(spec->stream_delay);
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
hinfo);
}
@@ -1922,9 +2296,14 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = spec->adc_nids[substream->number];
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
+ if (spec->powerdown_adcs) {
+ msleep(40);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ }
+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
return 0;
}
@@ -1933,8 +2312,12 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = spec->adc_nids[substream->number];
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
+ snd_hda_codec_cleanup_stream(codec, nid);
+ if (spec->powerdown_adcs)
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
return 0;
}
@@ -2185,6 +2568,8 @@ enum {
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
STAC_CTL_WIDGET_MONO_MUX,
+ STAC_CTL_WIDGET_AMP_MUX,
+ STAC_CTL_WIDGET_AMP_VOL,
STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
STAC_CTL_WIDGET_CLFE_SWITCH
@@ -2194,13 +2579,16 @@ 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_AMP_MUX,
+ STAC_AMP_VOL(NULL, 0, 0, 0, 0),
STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0),
};
/* add dynamic controls */
-static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
+static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type,
+ int idx, const char *name, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -2220,6 +2608,7 @@ static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char
knew = &spec->kctl_alloc[spec->num_kctl_used];
*knew = stac92xx_control_templates[type];
+ knew->index = idx;
knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name)
return -ENOMEM;
@@ -2228,6 +2617,14 @@ static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char
return 0;
}
+
+/* add dynamic controls */
+static int stac92xx_add_control(struct sigmatel_spec *spec, int type,
+ const char *name, unsigned long val)
+{
+ return stac92xx_add_control_idx(spec, type, 0, name, val);
+}
+
/* flag inputs as additional dynamic lineouts */
static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
{
@@ -2419,7 +2816,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side"
};
- hda_nid_t nid;
+ hda_nid_t nid = 0;
int i, err;
struct sigmatel_spec *spec = codec->spec;
@@ -2459,6 +2856,10 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
}
}
+ if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
+ cfg->hp_outs && !spec->multiout.hp_nid)
+ spec->multiout.hp_nid = nid;
+
if (cfg->hp_outs > 1) {
err = stac92xx_add_control(spec,
STAC_CTL_WIDGET_HP_SWITCH,
@@ -2571,8 +2972,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
}
/* labels for mono mux outputs */
-static const char *stac92xx_mono_labels[3] = {
- "DAC0", "DAC1", "Mixer"
+static const char *stac92xx_mono_labels[4] = {
+ "DAC0", "DAC1", "Mixer", "DAC2"
};
/* create mono mux for mono out on capable codecs */
@@ -2601,6 +3002,116 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
"Mono Mux", spec->mono_nid);
}
+/* labels for amp mux outputs */
+static const char *stac92xx_amp_labels[3] = {
+ "Front Microphone", "Microphone", "Line In",
+};
+
+/* create amp out controls mux on capable codecs */
+static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *amp_mux = &spec->private_amp_mux;
+ int i, err;
+
+ for (i = 0; i < spec->num_amps; i++) {
+ amp_mux->items[amp_mux->num_items].label =
+ stac92xx_amp_labels[i];
+ amp_mux->items[amp_mux->num_items].index = i;
+ amp_mux->num_items++;
+ }
+
+ if (spec->num_amps > 1) {
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX,
+ "Amp Selector Capture Switch", 0);
+ if (err < 0)
+ return err;
+ }
+ return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL,
+ "Amp Capture Volume",
+ HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT));
+}
+
+
+/* create PC beep volume controls */
+static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ int err;
+
+ /* check for mute support for the the amp */
+ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+ "PC Beep Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
+ 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,
+ "PC Beep Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int wcaps, nid, i, err = 0;
+
+ for (i = 0; i < spec->num_muxes; i++) {
+ nid = spec->mux_nids[i];
+ wcaps = get_wcaps(codec, nid);
+
+ if (wcaps & AC_WCAP_OUT_AMP) {
+ err = stac92xx_add_control_idx(spec,
+ STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+};
+
+static const char *stac92xx_spdif_labels[3] = {
+ "Digital Playback", "Analog Mux 1", "Analog Mux 2",
+};
+
+static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *spdif_mux = &spec->private_smux;
+ const char **labels = spec->spdif_labels;
+ int i, num_cons;
+ hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+
+ num_cons = snd_hda_get_connections(codec,
+ spec->smux_nids[0],
+ con_lst,
+ HDA_MAX_NUM_INPUTS);
+ if (!num_cons)
+ return -EINVAL;
+
+ if (!labels)
+ labels = stac92xx_spdif_labels;
+
+ for (i = 0; i < num_cons; i++) {
+ spdif_mux->items[spdif_mux->num_items].label = labels[i];
+ spdif_mux->items[spdif_mux->num_items].index = i;
+ spdif_mux->num_items++;
+ }
+
+ return 0;
+}
+
/* labels for dmic mux inputs */
static const char *stac92xx_dmic_labels[5] = {
"Analog Inputs", "Digital Mic 1", "Digital Mic 2",
@@ -2648,16 +3159,19 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
}
continue;
found:
- wcaps = get_wcaps(codec, nid);
+ wcaps = get_wcaps(codec, nid) &
+ (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
- if (wcaps & AC_WCAP_OUT_AMP) {
+ if (wcaps) {
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));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ (wcaps & AC_WCAP_OUT_AMP) ?
+ HDA_OUTPUT : HDA_INPUT));
if (err < 0)
return err;
}
@@ -2781,8 +3295,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
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;
+ int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
+ (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
u32 caps = query_amp_caps(codec,
spec->autocfg.mono_out_pin, dir);
hda_nid_t conn_list[1];
@@ -2804,21 +3318,26 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
!(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 (dir) {
+ hda_nid_t nid = spec->autocfg.mono_out_pin;
+
+ /* most mono outs have a least a mute/unmute switch */
+ dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+ "Mono Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
if (err < 0)
return err;
+ /* check for 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(nid, 1, 0, dir));
+ if (err < 0)
+ return err;
+ }
}
stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
@@ -2836,6 +3355,28 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (err < 0)
return err;
+ /* setup analog beep controls */
+ if (spec->anabeep_nid > 0) {
+ err = stac92xx_auto_create_beep_ctls(codec,
+ spec->anabeep_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* setup digital beep controls and input device */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ if (spec->digbeep_nid > 0) {
+ hda_nid_t nid = spec->digbeep_nid;
+
+ err = stac92xx_auto_create_beep_ctls(codec, nid);
+ if (err < 0)
+ return err;
+ err = snd_hda_attach_beep_device(codec, nid);
+ if (err < 0)
+ return err;
+ }
+#endif
+
if (hp_speaker_swap == 1) {
/* Restore the hp_outs and line_outs */
memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
@@ -2864,11 +3405,25 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (err < 0)
return err;
}
-
- if (spec->num_dmics > 0)
+ if (spec->num_amps > 0) {
+ err = stac92xx_auto_create_amp_output_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+ if (spec->num_dmics > 0 && !spec->dinput_mux)
if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
&spec->autocfg)) < 0)
return err;
+ if (spec->num_muxes > 0) {
+ err = stac92xx_auto_create_mux_input_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+ if (spec->num_smuxes > 0) {
+ err = stac92xx_auto_create_spdif_mux_ctls(codec);
+ if (err < 0)
+ return err;
+ }
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->multiout.max_channels > 2)
@@ -2876,17 +3431,17 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = dig_out;
- if (spec->autocfg.dig_in_pin)
+ if (dig_in && spec->autocfg.dig_in_pin)
spec->dig_in_nid = dig_in;
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
spec->input_mux = &spec->private_imux;
- if (!spec->dinput_mux)
- spec->dinput_mux = &spec->private_dimux;
+ spec->dinput_mux = &spec->private_dimux;
+ spec->sinput_mux = &spec->private_smux;
spec->mono_mux = &spec->private_mono_mux;
-
+ spec->amp_mux = &spec->private_amp_mux;
return 1;
}
@@ -3066,6 +3621,12 @@ static int stac92xx_init(struct hda_codec *codec)
snd_hda_sequence_write(codec, spec->init);
+ /* power down adcs initially */
+ if (spec->powerdown_adcs)
+ for (i = 0; i < spec->num_adcs; i++)
+ snd_hda_codec_write_cache(codec,
+ spec->adc_nids[i], 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
/* set up pins */
if (spec->hp_detect) {
/* Enable unsolicited responses on the HP widget */
@@ -3087,7 +3648,12 @@ static int stac92xx_init(struct hda_codec *codec)
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = cfg->input_pins[i];
if (nid) {
- unsigned int pinctl = AC_PINCTL_IN_EN;
+ unsigned int pinctl = snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* if PINCTL already set then skip */
+ if (pinctl & AC_PINCAP_IN)
+ continue;
+ pinctl = AC_PINCTL_IN_EN;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
pinctl |= stac92xx_get_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
@@ -3103,13 +3669,16 @@ static int stac92xx_init(struct hda_codec *codec)
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = get_defcfg_connect(def_conf);
/* 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;
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED)
+ /* skip any ports that don't have jacks since presence
+ * detection is useless */
+ if (def_conf && def_conf != AC_JACK_PORT_FIXED)
continue;
enable_pin_detect(codec, spec->pwr_nids[i], event | i);
codec->patch_ops.unsol_event(codec, (event | i) << 26);
@@ -3147,6 +3716,7 @@ static void stac92xx_free(struct hda_codec *codec)
kfree(spec->bios_pin_configs);
kfree(spec);
+ snd_hda_detach_beep_device(codec);
}
static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -3268,7 +3838,12 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
& 0x000000ff;
presence = get_hp_pin_presence(codec, nid);
- idx = 1 << idx;
+
+ /* several codecs have two power down bits */
+ if (spec->pwr_mapping)
+ idx = spec->pwr_mapping[idx];
+ else
+ idx = 1 << idx;
if (presence)
val &= ~idx;
@@ -3284,13 +3859,22 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
struct sigmatel_spec *spec = codec->spec;
int idx = res >> 26 & 0x0f;
- switch ((res >> 26) & 0x30) {
+ switch ((res >> 26) & 0x70) {
case STAC_HP_EVENT:
stac92xx_hp_detect(codec, res);
/* fallthru */
case STAC_PWR_EVENT:
if (spec->num_pwrs > 0)
stac92xx_pin_sense(codec, idx);
+ break;
+ case STAC_VREF_EVENT: {
+ int data = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ /* toggle VREF state based on GPIOx status */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
+ !!(data & (1 << idx)));
+ break;
+ }
}
}
@@ -3467,9 +4051,9 @@ static struct hda_input_mux stac92hd73xx_dmux = {
.num_items = 4,
.items = {
{ "Analog Inputs", 0x0b },
- { "CD", 0x08 },
{ "Digital Mic 1", 0x09 },
{ "Digital Mic 2", 0x0a },
+ { "CD", 0x08 },
}
};
@@ -3484,6 +4068,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
spec->pin_nids = stac92hd73xx_pin_nids;
spec->board_config = snd_hda_check_board_config(codec,
@@ -3516,17 +4101,14 @@ again:
switch (spec->multiout.num_dacs) {
case 0x3: /* 6 Channel */
- spec->multiout.hp_nid = 0x17;
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;
};
@@ -3535,27 +4117,34 @@ again:
spec->aloopback_mask = 0x01;
spec->aloopback_shift = 8;
+ spec->digbeep_nid = 0x1c;
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->smux_nids = stac92hd73xx_smux_nids;
+ spec->amp_nids = stac92hd73xx_amp_nids;
+ spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids);
spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
- spec->dinput_mux = &stac92hd73xx_dmux;
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
+ memcpy(&spec->private_dimux, &stac92hd73xx_dmux,
+ sizeof(stac92hd73xx_dmux));
switch (spec->board_config) {
case STAC_DELL_M6:
spec->init = dell_eq_core_init;
+ spec->num_smuxes = 0;
+ spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
+ spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
+ spec->num_amps = 1;
switch (codec->subsystem_id) {
case 0x1028025e: /* Analog Mics */
case 0x1028025f:
stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
spec->num_dmics = 0;
+ spec->private_dimux.num_items = 1;
break;
case 0x10280271: /* Digital Mics */
case 0x10280272:
@@ -3565,23 +4154,32 @@ again:
case 0x10280255:
stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
+ spec->private_dimux.num_items = 2;
break;
case 0x10280256: /* Both */
case 0x10280057:
stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
+ spec->private_dimux.num_items = 2;
break;
}
break;
default:
spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+ spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
}
+ if (spec->board_config > STAC_92HD73XX_REF) {
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+ }
+ spec->dinput_mux = &spec->private_dimux;
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
- err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
+ err = stac92xx_parse_auto_config(codec, 0x25, 0x27);
if (!err) {
if (spec->board_config < 0) {
@@ -3603,6 +4201,146 @@ again:
return 0;
}
+static struct hda_input_mux stac92hd83xxx_dmux = {
+ .num_items = 3,
+ .items = {
+ { "Analog Inputs", 0x03 },
+ { "Digital Mic 1", 0x04 },
+ { "Digital Mic 2", 0x05 },
+ }
+};
+
+static int patch_stac92hd83xxx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
+ spec->mono_nid = 0x19;
+ spec->digbeep_nid = 0x21;
+ spec->dmic_nids = stac92hd83xxx_dmic_nids;
+ spec->dmux_nids = stac92hd83xxx_dmux_nids;
+ spec->adc_nids = stac92hd83xxx_adc_nids;
+ spec->pwr_nids = stac92hd83xxx_pwr_nids;
+ spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
+ spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
+ spec->multiout.dac_nids = stac92hd83xxx_dac_nids;
+
+ spec->init = stac92hd83xxx_core_init;
+ switch (codec->vendor_id) {
+ case 0x111d7605:
+ spec->multiout.num_dacs = STAC92HD81_DAC_COUNT;
+ break;
+ default:
+ spec->num_pwrs--;
+ spec->init++; /* switch to config #2 */
+ spec->multiout.num_dacs = STAC92HD83_DAC_COUNT;
+ }
+
+ spec->mixer = stac92hd83xxx_mixer;
+ spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids);
+ spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
+ spec->num_dmics = STAC92HD83XXX_NUM_DMICS;
+ spec->dinput_mux = &stac92hd83xxx_dmux;
+ spec->pin_nids = stac92hd83xxx_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_92HD83XXX_MODELS,
+ stac92hd83xxx_models,
+ stac92hd83xxx_cfg_tbl);
+again:
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+ " STAC92HD83XXX, 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 = stac92hd83xxx_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
+
+ err = stac92xx_parse_auto_config(codec, 0x1d, 0);
+ 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_92HD83XXX_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+ return 0;
+}
+
+#ifdef SND_HDA_NEEDS_RESUME
+static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_POWER_STATE, pwr);
+
+ msleep(1);
+ for (i = 0; i < spec->num_adcs; i++) {
+ snd_hda_codec_write_cache(codec,
+ spec->adc_nids[i], 0,
+ AC_VERB_SET_POWER_STATE, pwr);
+ }
+};
+
+static int stac92hd71xx_resume(struct hda_codec *codec)
+{
+ stac92hd71xx_set_power_state(codec, AC_PWRST_D0);
+ return stac92xx_resume(codec);
+}
+
+static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
+ return 0;
+};
+
+#endif
+
+static struct hda_codec_ops stac92hd71bxx_patch_ops = {
+ .build_controls = stac92xx_build_controls,
+ .build_pcms = stac92xx_build_pcms,
+ .init = stac92xx_init,
+ .free = stac92xx_free,
+ .unsol_event = stac92xx_unsol_event,
+#ifdef SND_HDA_NEEDS_RESUME
+ .resume = stac92hd71xx_resume,
+ .suspend = stac92hd71xx_suspend,
+#endif
+};
+
+static struct hda_input_mux stac92hd71bxx_dmux = {
+ .num_items = 4,
+ .items = {
+ { "Analog Inputs", 0x00 },
+ { "Mixer", 0x01 },
+ { "Digital Mic 1", 0x02 },
+ { "Digital Mic 2", 0x03 },
+ }
+};
+
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -3613,8 +4351,12 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ codec->patch_ops = stac92xx_patch_ops;
spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
spec->pin_nids = stac92hd71bxx_pin_nids;
+ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux,
+ sizeof(stac92hd71bxx_dmux));
spec->board_config = snd_hda_check_board_config(codec,
STAC_92HD71BXX_MODELS,
stac92hd71bxx_models,
@@ -3641,37 +4383,101 @@ again:
case 0x111d76b5:
spec->mixer = stac92hd71bxx_mixer;
spec->init = stac92hd71bxx_core_init;
+ codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+ break;
+ case 0x111d7608: /* 5 Port with Analog Mixer */
+ switch (codec->subsystem_id) {
+ case 0x103c361a:
+ /* Enable VREF power saving on GPIO1 detect */
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ (AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+ spec->gpio_mask |= 0x02;
+ break;
+ }
+ if ((codec->revision_id & 0xf) == 0 ||
+ (codec->revision_id & 0xf) == 1) {
+#ifdef SND_HDA_NEEDS_RESUME
+ codec->patch_ops = stac92hd71bxx_patch_ops;
+#endif
+ spec->stream_delay = 40; /* 40 milliseconds */
+ }
+
+ /* no output amps */
+ spec->num_pwrs = 0;
+ spec->mixer = stac92hd71bxx_analog_mixer;
+ spec->dinput_mux = &spec->private_dimux;
+
+ /* disable VSW */
+ spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
+ stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
break;
+ case 0x111d7603: /* 6 Port with Analog Mixer */
+ if ((codec->revision_id & 0xf) == 1) {
+#ifdef SND_HDA_NEEDS_RESUME
+ codec->patch_ops = stac92hd71bxx_patch_ops;
+#endif
+ spec->stream_delay = 40; /* 40 milliseconds */
+ }
+
+ /* no output amps */
+ spec->num_pwrs = 0;
+ /* fallthru */
default:
+ spec->dinput_mux = &spec->private_dimux;
spec->mixer = stac92hd71bxx_analog_mixer;
spec->init = stac92hd71bxx_analog_core_init;
+ codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
}
- spec->aloopback_mask = 0x20;
+ spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
- /* GPIO0 High = EAPD */
- spec->gpio_mask = 0x01;
- spec->gpio_dir = 0x01;
- spec->gpio_mask = 0x01;
- spec->gpio_data = 0x01;
+ if (spec->board_config > STAC_92HD71BXX_REF) {
+ /* GPIO0 = EAPD */
+ spec->gpio_mask = 0x01;
+ spec->gpio_dir = 0x01;
+ spec->gpio_data = 0x01;
+ }
+ spec->powerdown_adcs = 1;
+ spec->digbeep_nid = 0x26;
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->smux_nids = stac92hd71bxx_smux_nids;
+ spec->pwr_nids = stac92hd71bxx_pwr_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;
+ switch (spec->board_config) {
+ case STAC_HP_M4:
+ spec->num_dmics = 0;
+ spec->num_smuxes = 0;
+ spec->num_dmuxes = 0;
+
+ /* enable internal microphone */
+ stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+ stac92xx_auto_set_pinctl(codec, 0x0e,
+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
+ break;
+ default:
+ spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
+ spec->num_smuxes = ARRAY_SIZE(stac92hd71bxx_smux_nids);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+ };
spec->multiout.num_dacs = 1;
spec->multiout.hp_nid = 0x11;
spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+ if (spec->dinput_mux)
+ spec->private_dimux.num_items +=
+ spec->num_dmics -
+ (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1);
err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
if (!err) {
@@ -3689,8 +4495,6 @@ again:
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
-
return 0;
};
@@ -3709,7 +4513,7 @@ static int patch_stac922x(struct hda_codec *codec)
spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
stac922x_models,
stac922x_cfg_tbl);
- if (spec->board_config == STAC_INTEL_MAC_V3) {
+ if (spec->board_config == STAC_INTEL_MAC_AUTO) {
spec->gpio_mask = spec->gpio_dir = 0x03;
spec->gpio_data = 0x03;
/* Intel Macs have all same PCI SSID, so we need to check
@@ -3741,6 +4545,9 @@ static int patch_stac922x(struct hda_codec *codec)
case 0x106b2200:
spec->board_config = STAC_INTEL_MAC_V5;
break;
+ default:
+ spec->board_config = STAC_INTEL_MAC_V3;
+ break;
}
}
@@ -3829,10 +4636,14 @@ static int patch_stac927x(struct hda_codec *codec)
stac92xx_set_config_regs(codec);
}
+ spec->digbeep_nid = 0x23;
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->smux_nids = stac927x_smux_nids;
+ spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids);
+ spec->spdif_labels = stac927x_spdif_labels;
spec->dac_list = stac927x_dac_nids;
spec->multiout.dac_nids = spec->dac_nids;
@@ -3875,9 +4686,11 @@ static int patch_stac927x(struct hda_codec *codec)
spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
break;
default:
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
+ if (spec->board_config > STAC_D965_REF) {
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
+ }
spec->num_dmics = 0;
spec->init = stac927x_core_init;
@@ -3949,10 +4762,13 @@ static int patch_stac9205(struct hda_codec *codec)
stac92xx_set_config_regs(codec);
}
+ spec->digbeep_nid = 0x23;
spec->adc_nids = stac9205_adc_nids;
spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
spec->mux_nids = stac9205_mux_nids;
spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
+ spec->smux_nids = stac9205_smux_nids;
+ spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);
spec->dmic_nids = stac9205_dmic_nids;
spec->num_dmics = STAC9205_NUM_DMICS;
spec->dmux_nids = stac9205_dmux_nids;
@@ -3988,6 +4804,9 @@ static int patch_stac9205(struct hda_codec *codec)
*/
spec->gpio_data = 0x01;
break;
+ case STAC_9205_REF:
+ /* SPDIF-In enabled */
+ break;
default:
/* GPIO0 High = EAPD */
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
@@ -4306,10 +5125,13 @@ 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 = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
+ { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
{ .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 },
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index e7e43524f8c..63e4871e5d8 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1,10 +1,10 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for VIA VT1708 codec
+ * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
*
- * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com>
- * Takashi Iwai <tiwai@suse.de>
+ * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
+ * Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,13 @@
/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
/* 2007-09-17 Lydia Wang Add VT1708B codec support */
+/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
+/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
+/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
+/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
+/* 2008-04-09 Lydia Wang Add Independent HP feature */
+/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
+/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -37,6 +44,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/asoundef.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_patch.h"
@@ -53,6 +61,8 @@
#define VT1708_DIGOUT_NID 0x14
#define VT1708_DIGIN_NID 0x16
#define VT1708_DIGIN_PIN 0x26
+#define VT1708_HP_PIN_NID 0x20
+#define VT1708_CD_PIN_NID 0x24
#define VT1709_HP_DAC_NID 0x28
#define VT1709_DIGOUT_NID 0x13
@@ -64,12 +74,64 @@
#define VT1708B_DIGIN_NID 0x15
#define VT1708B_DIGIN_PIN 0x21
+#define VT1708S_HP_NID 0x25
+#define VT1708S_DIGOUT_NID 0x12
+
+#define VT1702_HP_NID 0x17
+#define VT1702_DIGOUT_NID 0x11
+
#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)
#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)
#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)
#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723)
#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727)
+#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397)
+#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398)
+
+enum VIA_HDA_CODEC {
+ UNKNOWN = -1,
+ VT1708,
+ VT1709_10CH,
+ VT1709_6CH,
+ VT1708B_8CH,
+ VT1708B_4CH,
+ VT1708S,
+ VT1702,
+ CODEC_TYPES,
+};
+
+static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
+{
+ u16 ven_id = vendor_id >> 16;
+ u16 dev_id = vendor_id & 0xffff;
+ enum VIA_HDA_CODEC codec_type;
+
+ /* get codec type */
+ if (ven_id != 0x1106)
+ codec_type = UNKNOWN;
+ else if (dev_id >= 0x1708 && dev_id <= 0x170b)
+ codec_type = VT1708;
+ else if (dev_id >= 0xe710 && dev_id <= 0xe713)
+ codec_type = VT1709_10CH;
+ else if (dev_id >= 0xe714 && dev_id <= 0xe717)
+ codec_type = VT1709_6CH;
+ else if (dev_id >= 0xe720 && dev_id <= 0xe723)
+ codec_type = VT1708B_8CH;
+ else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+ codec_type = VT1708B_4CH;
+ else if ((dev_id & 0xfff) == 0x397
+ && (dev_id >> 12) < 8)
+ codec_type = VT1708S;
+ else if ((dev_id & 0xfff) == 0x398
+ && (dev_id >> 12) < 8)
+ codec_type = VT1702;
+ else
+ codec_type = UNKNOWN;
+ return codec_type;
+};
+#define VIA_HP_EVENT 0x01
+#define VIA_GPIO_EVENT 0x02
enum {
VIA_CTL_WIDGET_VOL,
@@ -77,12 +139,54 @@ enum {
};
enum {
- AUTO_SEQ_FRONT,
+ AUTO_SEQ_FRONT = 0,
AUTO_SEQ_SURROUND,
AUTO_SEQ_CENLFE,
AUTO_SEQ_SIDE
};
+#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
+
+/* Some VT1708S based boards gets the micboost setting wrong, so we have
+ * to apply some brute-force and re-write the TLV's by software. */
+static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+
+ if (get_codec_type(codec->vendor_id) == VT1708S
+ && (nid == 0x1a || nid == 0x1e)) {
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), _tlv + 1))
+ return -EFAULT;
+ if (put_user(0, _tlv + 2)) /* offset = 0 */
+ return -EFAULT;
+ if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+
+ if (get_codec_type(codec->vendor_id) == VT1708S
+ && (nid == 0x1a || nid == 0x1e)) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 3;
+ }
+ return 0;
+}
+
static struct snd_kcontrol_new vt1708_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
@@ -94,7 +198,8 @@ struct via_spec {
struct snd_kcontrol_new *mixers[3];
unsigned int num_mixers;
- struct hda_verb *init_verbs;
+ struct hda_verb *init_verbs[5];
+ unsigned int num_iverbs;
char *stream_name_analog;
struct hda_pcm_stream *stream_analog_playback;
@@ -106,6 +211,7 @@ struct via_spec {
/* playback */
struct hda_multi_out multiout;
+ hda_nid_t extra_dig_out_nid;
/* capture */
unsigned int num_adc_nids;
@@ -117,15 +223,19 @@ struct via_spec {
unsigned int cur_mux[3];
/* PCM information */
- struct hda_pcm pcm_rec[2];
+ struct hda_pcm pcm_rec[3];
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
unsigned int num_kctl_alloc, num_kctl_used;
struct snd_kcontrol_new *kctl_alloc;
- struct hda_input_mux private_imux;
+ struct hda_input_mux private_imux[2];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ /* HP mode source */
+ const struct hda_input_mux *hp_mux;
+ unsigned int hp_independent_mode;
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
@@ -146,6 +256,16 @@ static hda_nid_t vt1708B_adc_nids[2] = {
0x13, 0x14
};
+static hda_nid_t vt1708S_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x13, 0x14
+};
+
+static hda_nid_t vt1702_adc_nids[3] = {
+ /* ADC1-2 */
+ 0x12, 0x20, 0x1F
+};
+
/* add dynamic controls */
static int via_add_control(struct via_spec *spec, int type, const char *name,
unsigned long val)
@@ -283,19 +403,108 @@ 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)
+ 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 if (IS_VT1702_VENDORID(vendor_id) && (adc_idx == 0))
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 0x13, &spec->cur_mux[adc_idx]);
else
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
spec->adc_nids[adc_idx],
&spec->cur_mux[adc_idx]);
}
+static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->hp_mux, uinfo);
+}
+
+static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ hda_nid_t nid = spec->autocfg.hp_pins[0];
+ unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL,
+ 0x00);
+
+ ucontrol->value.enumerated.item[0] = pinsel;
+
+ return 0;
+}
+
+static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ hda_nid_t nid = spec->autocfg.hp_pins[0];
+ unsigned int pinsel = ucontrol->value.enumerated.item[0];
+ unsigned int con_nid = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+
+ if (con_nid == spec->multiout.hp_nid) {
+ if (pinsel == 0) {
+ if (!spec->hp_independent_mode) {
+ if (spec->multiout.num_dacs > 1)
+ spec->multiout.num_dacs -= 1;
+ spec->hp_independent_mode = 1;
+ }
+ } else if (pinsel == 1) {
+ if (spec->hp_independent_mode) {
+ if (spec->multiout.num_dacs > 1)
+ spec->multiout.num_dacs += 1;
+ spec->hp_independent_mode = 0;
+ }
+ }
+ } else {
+ if (pinsel == 0) {
+ if (spec->hp_independent_mode) {
+ if (spec->multiout.num_dacs > 1)
+ spec->multiout.num_dacs += 1;
+ spec->hp_independent_mode = 0;
+ }
+ } else if (pinsel == 1) {
+ if (!spec->hp_independent_mode) {
+ if (spec->multiout.num_dacs > 1)
+ spec->multiout.num_dacs -= 1;
+ spec->hp_independent_mode = 1;
+ }
+ }
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+ pinsel);
+
+ if (spec->multiout.hp_nid &&
+ spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT])
+ snd_hda_codec_setup_stream(codec,
+ spec->multiout.hp_nid,
+ 0, 0, 0);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new via_hp_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .count = 1,
+ .info = via_independent_hp_info,
+ .get = via_independent_hp_get,
+ .put = via_independent_hp_put,
+ },
+ { } /* end */
+};
+
/* capture mixer elements */
static struct snd_kcontrol_new vt1708_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -380,6 +589,138 @@ static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
+
+static void playback_multi_pcm_prep_0(struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ struct hda_multi_out *mout = &spec->multiout;
+ hda_nid_t *nids = mout->dac_nids;
+ int chs = substream->runtime->channels;
+ int i;
+
+ mutex_lock(&codec->spdif_mutex);
+ if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+ if (chs == 2 &&
+ snd_hda_is_supported_format(codec, mout->dig_out_nid,
+ format) &&
+ !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+ mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+ /* turn off SPDIF once; otherwise the IEC958 bits won't
+ * be updated */
+ if (codec->spdif_ctls & AC_DIG1_ENABLE)
+ snd_hda_codec_write(codec, mout->dig_out_nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls &
+ ~AC_DIG1_ENABLE & 0xff);
+ snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+ stream_tag, 0, format);
+ /* turn on again (if needed) */
+ if (codec->spdif_ctls & AC_DIG1_ENABLE)
+ snd_hda_codec_write(codec, mout->dig_out_nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & 0xff);
+ } else {
+ mout->dig_out_used = 0;
+ snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+ 0, 0, 0);
+ }
+ }
+ mutex_unlock(&codec->spdif_mutex);
+
+ /* front */
+ snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
+ 0, format);
+
+ if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
+ !spec->hp_independent_mode)
+ /* headphone out will just decode front left/right (stereo) */
+ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
+ 0, format);
+
+ /* extra outputs copied from front */
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+ if (mout->extra_out_nid[i])
+ snd_hda_codec_setup_stream(codec,
+ mout->extra_out_nid[i],
+ stream_tag, 0, format);
+
+ /* surrounds */
+ for (i = 1; i < mout->num_dacs; i++) {
+ if (chs >= (i + 1) * 2) /* independent out */
+ snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+ i * 2, format);
+ else /* copy front */
+ snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+ 0, format);
+ }
+}
+
+static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ struct hda_multi_out *mout = &spec->multiout;
+ hda_nid_t *nids = mout->dac_nids;
+
+ if (substream->number == 0)
+ playback_multi_pcm_prep_0(codec, stream_tag, format,
+ substream);
+ else {
+ if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
+ spec->hp_independent_mode)
+ snd_hda_codec_setup_stream(codec, mout->hp_nid,
+ stream_tag, 0, format);
+ }
+
+ return 0;
+}
+
+static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ struct hda_multi_out *mout = &spec->multiout;
+ hda_nid_t *nids = mout->dac_nids;
+ int i;
+
+ if (substream->number == 0) {
+ for (i = 0; i < mout->num_dacs; i++)
+ snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+
+ if (mout->hp_nid && !spec->hp_independent_mode)
+ snd_hda_codec_setup_stream(codec, mout->hp_nid,
+ 0, 0, 0);
+
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+ if (mout->extra_out_nid[i])
+ snd_hda_codec_setup_stream(codec,
+ mout->extra_out_nid[i],
+ 0, 0, 0);
+ mutex_lock(&codec->spdif_mutex);
+ if (mout->dig_out_nid &&
+ mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+ snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+ 0, 0, 0);
+ mout->dig_out_used = 0;
+ }
+ mutex_unlock(&codec->spdif_mutex);
+ } else {
+ if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
+ spec->hp_independent_mode)
+ snd_hda_codec_setup_stream(codec, mout->hp_nid,
+ 0, 0, 0);
+ }
+
+ return 0;
+}
+
/*
* Digital out
*/
@@ -399,6 +740,21 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
+/* setup SPDIF output stream */
+static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int stream_tag, unsigned int format)
+{
+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+ if (codec->spdif_ctls & AC_DIG1_ENABLE)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ /* turn on again (if needed) */
+ if (codec->spdif_ctls & AC_DIG1_ENABLE)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & 0xff);
+}
+
static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@@ -406,8 +762,20 @@ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
+ hda_nid_t nid;
+
+ /* 1st or 2nd S/PDIF */
+ if (substream->number == 0)
+ nid = spec->multiout.dig_out_nid;
+ else if (substream->number == 1)
+ nid = spec->extra_dig_out_nid;
+ else
+ return -1;
+
+ mutex_lock(&codec->spdif_mutex);
+ setup_dig_playback_stream(codec, nid, stream_tag, format);
+ mutex_unlock(&codec->spdif_mutex);
+ return 0;
}
/*
@@ -436,14 +804,14 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
}
static struct hda_pcm_stream vt1708_pcm_analog_playback = {
- .substreams = 1,
+ .substreams = 2,
.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
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
},
};
@@ -515,6 +883,13 @@ static int via_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
spec->multiout.share_spdif = 1;
+
+ if (spec->extra_dig_out_nid) {
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->extra_dig_out_nid);
+ if (err < 0)
+ return err;
+ }
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -580,10 +955,89 @@ static void via_free(struct hda_codec *codec)
kfree(codec->spec);
}
+/* mute internal speaker if HP is plugged */
+static void via_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ struct via_spec *spec = codec->spec;
+
+ present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ present ? HDA_AMP_MUTE : 0);
+}
+
+static void via_gpio_control(struct hda_codec *codec)
+{
+ unsigned int gpio_data;
+ unsigned int vol_counter;
+ unsigned int vol;
+ unsigned int master_vol;
+
+ struct via_spec *spec = codec->spec;
+
+ gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & 0x03;
+
+ vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
+ 0xF84, 0) & 0x3F0000) >> 16;
+
+ vol = vol_counter & 0x1F;
+ master_vol = snd_hda_codec_read(codec, 0x1A, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_INPUT);
+
+ if (gpio_data == 0x02) {
+ /* unmute line out */
+ snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+
+ if (vol_counter & 0x20) {
+ /* decrease volume */
+ if (vol > master_vol)
+ vol = master_vol;
+ snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
+ 0, HDA_AMP_VOLMASK,
+ master_vol-vol);
+ } else {
+ /* increase volume */
+ snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
+ HDA_AMP_VOLMASK,
+ ((master_vol+vol) > 0x2A) ? 0x2A :
+ (master_vol+vol));
+ }
+ } else if (!(gpio_data & 0x02)) {
+ /* mute line out */
+ snd_hda_codec_amp_stereo(codec,
+ spec->autocfg.line_out_pins[0],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ HDA_AMP_MUTE);
+ }
+}
+
+/* unsolicited event for jack sensing */
+static void via_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ res >>= 26;
+ if (res == VIA_HP_EVENT)
+ via_hp_automute(codec);
+ else if (res == VIA_GPIO_EVENT)
+ via_gpio_control(codec);
+}
+
+static hda_nid_t slave_dig_outs[] = {
+ 0,
+};
+
static int via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- snd_hda_sequence_write(codec, spec->init_verbs);
+ int i;
+ for (i = 0; i < spec->num_iverbs; i++)
+ snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
/* Lydia Add for EAPD enable */
if (!spec->dig_in_nid) { /* No Digital In connection */
if (IS_VT1708_VENDORID(codec->vendor_id)) {
@@ -611,6 +1065,9 @@ static int via_init(struct hda_codec *codec)
snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+ /* no slave outs */
+ codec->slave_dig_outs = slave_dig_outs;
+
return 0;
}
@@ -657,10 +1114,10 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
spec->multiout.dac_nids[i] = 0x12;
break;
case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x13;
+ spec->multiout.dac_nids[i] = 0x11;
break;
case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x11;
+ spec->multiout.dac_nids[i] = 0x13;
break;
}
}
@@ -685,7 +1142,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
continue;
if (i != AUTO_SEQ_FRONT)
- nid_vol = 0x1b - i + 1;
+ nid_vol = 0x18 + i;
if (i == AUTO_SEQ_CENLFE) {
/* Center/LFE */
@@ -760,6 +1217,24 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
return 0;
}
+static void create_hp_imux(struct via_spec *spec)
+{
+ int i;
+ struct hda_input_mux *imux = &spec->private_imux[1];
+ static const char *texts[] = { "OFF", "ON", NULL};
+
+ /* for hp mode select */
+ i = 0;
+ while (texts[i] != NULL) {
+ imux->items[imux->num_items].label = texts[i];
+ imux->items[imux->num_items].index = i;
+ imux->num_items++;
+ i++;
+ }
+
+ spec->hp_mux = &spec->private_imux[1];
+}
+
static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
{
int err;
@@ -780,6 +1255,8 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
if (err < 0)
return err;
+ create_hp_imux(spec);
+
return 0;
}
@@ -790,7 +1267,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
static char *labels[] = {
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
};
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
@@ -840,11 +1317,36 @@ static struct hda_amp_list vt1708_loopbacks[] = {
};
#endif
+static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf;
+ unsigned char seqassoc;
+
+ def_conf = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ seqassoc = (unsigned char) get_defcfg_association(def_conf);
+ seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
+ if (seqassoc == 0xff) {
+ def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+ def_conf >> 24);
+ }
+ }
+
+ return;
+}
+
static int vt1708_parse_auto_config(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int err;
+ /* Add HP and CD pin config connect bit re-config action */
+ vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
+ vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
+
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
@@ -874,9 +1376,12 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
- spec->init_verbs = vt1708_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
+
+ spec->input_mux = &spec->private_imux[0];
- spec->input_mux = &spec->private_imux;
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
return 1;
}
@@ -897,7 +1402,7 @@ static int patch_vt1708(struct hda_codec *codec)
int err;
/* create a codec specific record */
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -966,6 +1471,11 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
{ } /* end */
};
+static struct hda_verb vt1709_uniwill_init_verbs[] = {
+ {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -1090,11 +1600,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
break;
case AUTO_SEQ_SURROUND:
/* AOW3 */
- spec->multiout.dac_nids[i] = 0x27;
+ spec->multiout.dac_nids[i] = 0x11;
break;
case AUTO_SEQ_SIDE:
/* AOW1 */
- spec->multiout.dac_nids[i] = 0x11;
+ spec->multiout.dac_nids[i] = 0x27;
break;
default:
break;
@@ -1203,26 +1713,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
} else if (i == AUTO_SEQ_SURROUND) {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+ 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(0x29, 3, 0,
+ HDA_COMPOSE_AMP_VAL(0x1a, 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_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(0x1a, 3, 0,
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
@@ -1265,7 +1775,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
static char *labels[] = {
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
};
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
@@ -1339,7 +1849,10 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
return 1;
}
@@ -1360,7 +1873,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
int err;
/* create a codec specific record */
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -1375,7 +1888,8 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
"Using genenic mode...\n");
}
- spec->init_verbs = vt1709_10ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
spec->stream_name_analog = "VT1709 Analog";
spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
@@ -1396,6 +1910,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1709_loopbacks;
#endif
@@ -1451,7 +1966,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
int err;
/* create a codec specific record */
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -1466,7 +1981,8 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
"Using genenic mode...\n");
}
- spec->init_verbs = vt1709_6ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
spec->stream_name_analog = "VT1709 Analog";
spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
@@ -1487,6 +2003,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1709_loopbacks;
#endif
@@ -1586,27 +2103,32 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
{ }
};
+static struct hda_verb vt1708B_uniwill_init_verbs[] = {
+ {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
- .substreams = 1,
+ .substreams = 2,
.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
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
},
};
static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
- .substreams = 1,
+ .substreams = 2,
.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
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
},
};
@@ -1662,10 +2184,10 @@ static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
spec->multiout.dac_nids[i] = 0x24;
break;
case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x25;
+ spec->multiout.dac_nids[i] = 0x11;
break;
case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x11;
+ spec->multiout.dac_nids[i] = 0x25;
break;
}
}
@@ -1680,7 +2202,7 @@ static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
{
char name[32];
static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18};
+ hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
hda_nid_t nid, nid_vol = 0;
int i, err;
@@ -1785,6 +2307,8 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
if (err < 0)
return err;
+ create_hp_imux(spec);
+
return 0;
}
@@ -1795,7 +2319,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
static char *labels[] = {
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
};
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
@@ -1869,7 +2393,10 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
return 1;
}
@@ -1890,7 +2417,7 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)
int err;
/* create a codec specific record */
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -1906,7 +2433,8 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)
"from BIOS. Using genenic mode...\n");
}
- spec->init_verbs = vt1708B_8ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
spec->stream_name_analog = "VT1708B Analog";
spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
@@ -1926,6 +2454,7 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1708B_loopbacks;
#endif
@@ -1939,7 +2468,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
int err;
/* create a codec specific record */
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -1955,7 +2484,8 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
"from BIOS. Using genenic mode...\n");
}
- spec->init_verbs = vt1708B_4ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
spec->stream_name_analog = "VT1708B Analog";
spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
@@ -1975,6 +2505,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1708B_loopbacks;
#endif
@@ -1982,6 +2513,752 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
return 0;
}
+/* Patch for VT1708S */
+
+/* VT1708S software backdoor based override for buggy hardware micboost
+ * setting */
+#define MIC_BOOST_VOLUME(xname, nid) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = mic_boost_volume_info, \
+ .get = snd_hda_mixer_amp_volume_get, \
+ .put = snd_hda_mixer_amp_volume_put, \
+ .tlv = { .c = mic_boost_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) }
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
+ MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A),
+ MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E),
+ {
+ .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 */
+};
+
+static struct hda_verb vt1708S_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)},
+
+ /* Setup default input of PW4 to MW0 */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* PW9, PW10 Output enable */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Enable Mic Boost Volume backdoor */
+ {0x1, 0xf98, 0x1},
+ { }
+};
+
+static struct hda_verb vt1708S_uniwill_init_verbs[] = {
+ {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
+static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
+ .substreams = 2,
+ .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 vt1708S_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 vt1708S_pcm_digital_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare
+ },
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1708S_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] = 0x11;
+ break;
+ case AUTO_SEQ_SIDE:
+ spec->multiout.dac_nids[i] = 0x25;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1708S_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[] = {0x10, 0x11, 0x24, 0x25};
+ hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
+ hda_nid_t nid, nid_vol, nid_mute;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+
+ nid_vol = nid_vols[i];
+ nid_mute = nid_mutes[i];
+
+ if (i == AUTO_SEQ_CENLFE) {
+ /* Center/LFE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_mute,
+ 1, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_mute,
+ 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT) {
+ /* add control to mixer index 0 */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ /* Front */
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_mute,
+ 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_mute,
+ 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ create_hp_imux(spec);
+
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static char *labels[] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+ };
+ struct hda_input_mux *imux = &spec->private_imux[0];
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = 5;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x1a: /* Mic */
+ idx = 2;
+ break;
+
+ case 0x1b: /* Line In */
+ idx = 3;
+ break;
+
+ case 0x1e: /* Front Mic */
+ idx = 4;
+ break;
+
+ case 0x1f: /* CD */
+ idx = 1;
+ break;
+ }
+ err = via_new_analog_input(spec, 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-1;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1708S_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t vt1708s_ignore[] = {0x21, 0};
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ vt1708s_ignore);
+ if (err < 0)
+ return err;
+ err = vt1708S_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 = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1708S_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 = VT1708S_DIGOUT_NID;
+
+ spec->extra_dig_out_nid = 0x15;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1708S_loopbacks[] = {
+ { 0x16, HDA_INPUT, 1 },
+ { 0x16, HDA_INPUT, 2 },
+ { 0x16, HDA_INPUT, 3 },
+ { 0x16, HDA_INPUT, 4 },
+ { } /* end */
+};
+#endif
+
+static int patch_vt1708S(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1708S_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
+
+ spec->stream_name_analog = "VT1708S Analog";
+ spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1708S Digital";
+ spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1708S_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1708S_loopbacks;
+#endif
+
+ return 0;
+}
+
+/* Patch for VT1702 */
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1702_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 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 */
+};
+
+static struct hda_verb vt1702_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ */
+ /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /* Setup default input of PW4 to MW0 */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* PW6 PW7 Output enable */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ { }
+};
+
+static struct hda_verb vt1702_uniwill_init_verbs[] = {
+ {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
+static struct hda_pcm_stream vt1702_pcm_analog_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1702_pcm_analog_capture = {
+ .substreams = 3,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x12, /* NID to query formats and rates */
+ .ops = {
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1702_pcm_digital_playback = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare
+ },
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ if (cfg->line_out_pins[0]) {
+ /* config dac list */
+ spec->multiout.dac_nids[0] = 0x10;
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int err;
+
+ if (!cfg->line_out_pins[0])
+ return -1;
+
+ /* add control to mixer index 0 */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x1A, 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(0x1A, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ /* Front */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = 0x1D;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ create_hp_imux(spec);
+
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static char *labels[] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+ };
+ struct hda_input_mux *imux = &spec->private_imux[0];
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = 3;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x14: /* Mic */
+ idx = 1;
+ break;
+
+ case 0x15: /* Line In */
+ idx = 2;
+ break;
+
+ case 0x18: /* Front Mic */
+ idx = 3;
+ break;
+ }
+ err = via_new_analog_input(spec, cfg->input_pins[i],
+ labels[i], idx, 0x1A);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx-1;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1702_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t vt1702_ignore[] = {0x1C, 0};
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ vt1702_ignore);
+ if (err < 0)
+ return err;
+ err = vt1702_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 = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1702_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 = VT1702_DIGOUT_NID;
+
+ spec->extra_dig_out_nid = 0x1B;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->input_mux = &spec->private_imux[0];
+
+ if (spec->hp_mux)
+ spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1702_loopbacks[] = {
+ { 0x1A, HDA_INPUT, 1 },
+ { 0x1A, HDA_INPUT, 2 },
+ { 0x1A, HDA_INPUT, 3 },
+ { 0x1A, HDA_INPUT, 4 },
+ { } /* end */
+};
+#endif
+
+static int patch_vt1702(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+ unsigned int response;
+ unsigned char control;
+
+ /* create a codec specific record */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1702_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+ spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
+
+ spec->stream_name_analog = "VT1702 Analog";
+ spec->stream_analog_playback = &vt1702_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1702_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1702 Digital";
+ spec->stream_digital_playback = &vt1702_pcm_digital_playback;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1702_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1702_loopbacks;
+#endif
+
+ /* Open backdoor */
+ response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0);
+ control = (unsigned char)(response & 0xff);
+ control |= 0x3;
+ snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control);
+
+ /* Enable GPIO 0&1 for volume&mute control */
+ /* Enable GPIO 2 for DMIC-DATA */
+ response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0);
+ control = (unsigned char)((response >> 16) & 0x3f);
+ snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control);
+
+ return 0;
+}
+
/*
* patch entries
*/
@@ -2022,5 +3299,37 @@ struct hda_codec_preset snd_hda_preset_via[] = {
.patch = patch_vt1708B_4ch},
{ .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
.patch = patch_vt1708B_4ch},
+ { .id = 0x11060397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11061397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11062397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11063397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11064397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11065397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11066397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11067397, .name = "VIA VT1708S",
+ .patch = patch_vt1708S},
+ { .id = 0x11060398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11061398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11062398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11063398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11064398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11065398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11066398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
+ { .id = 0x11067398, .name = "VIA VT1702",
+ .patch = patch_vt1702},
{} /* terminator */
};
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
index dab31b2756a..03391da8c8c 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -59,7 +59,8 @@ static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip,
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
- snd_assert(chip >= 0 && chip < 4, return);
+ if (snd_BUG_ON(chip < 0 || chip >= 4))
+ return;
tmp = snd_ice1712_gpio_read(ice);
tmp |= priv->add_flags;
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index 868ae291b96..110d16e5273 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -44,10 +44,9 @@
* not working: prety much everything else, at least i could verify that
* we have no digital output, no capture, pretty bad clicks and poops
* on mixer switch and other coll stuff.
- *
- */
+ */
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -131,7 +130,7 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
snd_ice1712_gpio_write(ice, tmp);
udelay(50);
- /*
+ /*
* send i2c stop condition and start condition
* to obtain sane state
*/
@@ -152,10 +151,16 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
* skipping ack cycles inbetween
*/
for (j = 0; j < 3; j++) {
- switch(j) {
- case 0: val = dev; break;
- case 1: val = reg; break;
- case 2: val = data; break;
+ switch (j) {
+ case 0:
+ val = dev;
+ break;
+ case 1:
+ val = reg;
+ break;
+ case 2:
+ val = data;
+ break;
}
for (i = 7; i >= 0; i--) {
tmp &= ~AUREON_SPI_CLK;
@@ -171,7 +176,7 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
}
- tmp &= ~AUREON_SPI_CLK;
+ tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
tmp |= AUREON_SPI_CLK;
@@ -203,7 +208,7 @@ static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
- if(uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ 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;
@@ -231,12 +236,12 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
snd_ice1712_save_gpio_status(ice);
oval = spec->pca9554_out;
- if ((change = (oval != nval))) {
+ change = (oval != nval);
+ if (change) {
aureon_pca9554_write(ice, PCA9554_OUT, nval);
spec->pca9554_out = nval;
}
snd_ice1712_restore_gpio_status(ice);
-
return change;
}
@@ -256,7 +261,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
udelay(10);
tmp &= ~AUREON_AC97_ADDR;
snd_ice1712_gpio_write(ice, tmp);
- udelay(10);
+ udelay(10);
/* Send low-order byte to XILINX chip */
tmp &= ~AUREON_AC97_DATA_MASK;
@@ -269,7 +274,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
tmp &= ~AUREON_AC97_DATA_LOW;
snd_ice1712_gpio_write(ice, tmp);
udelay(10);
-
+
/* Send high-order byte to XILINX chip */
tmp &= ~AUREON_AC97_DATA_MASK;
tmp |= (val >> 8) & AUREON_AC97_DATA_MASK;
@@ -282,7 +287,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
tmp &= ~AUREON_AC97_DATA_HIGH;
snd_ice1712_gpio_write(ice, tmp);
udelay(10);
-
+
/* Instruct XILINX chip to parse the data to the STAC9744 chip */
tmp |= AUREON_AC97_COMMIT;
snd_ice1712_gpio_write(ice, tmp);
@@ -290,7 +295,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
tmp &= ~AUREON_AC97_COMMIT;
snd_ice1712_gpio_write(ice, tmp);
udelay(10);
-
+
/* Store the data in out private buffer */
spec->stac9744[(reg & 0x7F) >> 1] = val;
}
@@ -304,7 +309,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
/*
* Initialize STAC9744 chip
*/
-static int aureon_ac97_init (struct snd_ice1712 *ice)
+static int aureon_ac97_init(struct snd_ice1712 *ice)
{
struct aureon_spec *spec = ice->spec;
int i;
@@ -335,20 +340,21 @@ static int aureon_ac97_init (struct snd_ice1712 *ice)
tmp = (snd_ice1712_gpio_read(ice) | AUREON_AC97_RESET) & ~AUREON_AC97_DATA_MASK;
snd_ice1712_gpio_write(ice, tmp);
udelay(3);
-
+
tmp &= ~AUREON_AC97_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(3);
-
+
tmp |= AUREON_AC97_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(3);
-
+
memset(&spec->stac9744, 0, sizeof(spec->stac9744));
- for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2)
+ for (i = 0; ac97_defaults[i] != (unsigned short)-1; i += 2)
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
+
+ /* Unmute AC'97 master volume permanently - muting is done by WM8770 */
+ aureon_ac97_write(ice, AC97_MASTER, 0x0000);
return 0;
}
@@ -388,7 +394,7 @@ static int aureon_ac97_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short ovol, nvol;
int change;
-
+
snd_ice1712_save_gpio_status(ice);
ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
@@ -396,13 +402,14 @@ static int aureon_ac97_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
if (kcontrol->private_value & AUREON_AC97_STEREO)
nvol |= ((0x1F - ucontrol->value.integer.value[1]) << 8) & 0x1F00;
nvol |= ovol & ~0x1F1F;
-
- if ((change = (ovol != nvol)))
+
+ change = (ovol != nvol);
+ if (change)
aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
snd_ice1712_restore_gpio_status(ice);
- return change;
+ return change;
}
/*
@@ -416,7 +423,8 @@ static int aureon_ac97_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
mutex_lock(&ice->gpio_mutex);
- ucontrol->value.integer.value[0] = aureon_ac97_read(ice, kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
+ ucontrol->value.integer.value[0] = aureon_ac97_read(ice,
+ kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
mutex_unlock(&ice->gpio_mutex);
return 0;
@@ -429,13 +437,14 @@ static int aureon_ac97_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
int change;
snd_ice1712_save_gpio_status(ice);
-
+
ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
- nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~ 0x8000);
-
- if ((change = (ovol != nvol)))
+ nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~0x8000);
+
+ change = (ovol != nvol);
+ if (change)
aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
-
+
snd_ice1712_restore_gpio_status(ice);
return change;
@@ -465,13 +474,14 @@ static int aureon_ac97_micboost_put(struct snd_kcontrol *kcontrol, struct snd_ct
int change;
snd_ice1712_save_gpio_status(ice);
-
+
ovol = aureon_ac97_read(ice, AC97_MIC);
nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x0020) | (ovol & ~0x0020);
-
- if ((change = (ovol != nvol)))
+
+ change = (ovol != nvol);
+ if (change)
aureon_ac97_write(ice, AC97_MIC, nvol);
-
+
snd_ice1712_restore_gpio_status(ice);
return change;
@@ -493,16 +503,15 @@ static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned
snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_SPI_MOSI|PRODIGY_SPI_CLK|PRODIGY_WM_CS));
mosi = PRODIGY_SPI_MOSI;
clk = PRODIGY_SPI_CLK;
- }
- else {
+ } else {
snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RW|AUREON_SPI_MOSI|AUREON_SPI_CLK|
AUREON_WM_CS|AUREON_CS8415_CS));
mosi = AUREON_SPI_MOSI;
clk = AUREON_SPI_CLK;
-
+
tmp |= AUREON_WM_RW;
}
-
+
tmp &= ~cs;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
@@ -534,7 +543,9 @@ static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned
/*
* Read data in SPI mode
*/
-static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits, unsigned char *buffer, int size) {
+static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs,
+ unsigned int data, int bits, unsigned char *buffer, int size)
+{
int i, j;
unsigned int tmp;
@@ -544,7 +555,7 @@ static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned i
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
- for (i=bits-1; i>=0; i--) {
+ for (i = bits-1; i >= 0; i--) {
if (data & (1 << i))
tmp |= AUREON_SPI_MOSI;
else
@@ -561,9 +572,9 @@ static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned i
udelay(1);
}
- for (j=0; j<size; j++) {
+ for (j = 0; j < size; j++) {
unsigned char outdata = 0;
- for (i=7; i>=0; i--) {
+ for (i = 7; i >= 0; i--) {
tmp = snd_ice1712_gpio_read(ice);
outdata <<= 1;
outdata |= (tmp & AUREON_SPI_MISO) ? 1 : 0;
@@ -584,19 +595,24 @@ static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned i
snd_ice1712_gpio_write(ice, tmp);
}
-static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg) {
+static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg)
+{
unsigned char val;
aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, &val, 1);
return val;
}
-static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg, unsigned char *buffer, int size) {
+static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg,
+ unsigned char *buffer, int size)
+{
aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, buffer, size);
}
-static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg, unsigned char val) {
+static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg,
+ unsigned char val)
+{
aureon_spi_write(ice, AUREON_CS8415_CS, 0x200000 | (reg << 8) | val, 24);
}
@@ -654,18 +670,20 @@ static int aureon_ac97_mmute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return 0;
}
-static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short ovol, nvol;
int change;
-
+
snd_ice1712_save_gpio_status(ice);
-
+
ovol = wm_get(ice, WM_OUT_MUX1);
nvol = (ovol & ~0x02) | (ucontrol->value.integer.value[0] ? 0x02 : 0x00);
- if ((change = (ovol != nvol)))
+ change = (ovol != nvol);
+ if (change)
wm_put(ice, WM_OUT_MUX1, nvol);
-
+
snd_ice1712_restore_gpio_status(ice);
return change;
@@ -702,12 +720,12 @@ static const unsigned char wm_vol[256] = {
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 = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
-
+
wm_put(ice, index, nvol);
wm_put_nocache(ice, index, 0x180 | nvol);
}
@@ -736,7 +754,8 @@ static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
snd_ice1712_save_gpio_status(ice);
oval = wm_get(ice, WM_MUTE);
nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
- if ((change = (nval != oval)))
+ change = (oval != nval);
+ if (change)
wm_put(ice, WM_MUTE, nval);
snd_ice1712_restore_gpio_status(ice);
@@ -760,7 +779,7 @@ static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct aureon_spec *spec = ice->spec;
int i;
- for (i=0; i<2; i++)
+ for (i = 0; i < 2; i++)
ucontrol->value.integer.value[i] =
spec->master[i] & ~WM_VOL_MUTE;
return 0;
@@ -849,7 +868,8 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
/*
* WM8770 mute control
*/
-static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = kcontrol->private_value >> 8;
uinfo->value.integer.min = 0;
@@ -862,7 +882,7 @@ static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
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;
@@ -907,7 +927,7 @@ static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct aureon_spec *spec = ice->spec;
-
+
ucontrol->value.integer.value[0] =
(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
ucontrol->value.integer.value[1] =
@@ -1083,21 +1103,21 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[] = {
- "CD", //AIN1
- "Aux", //AIN2
- "Line", //AIN3
- "Mic", //AIN4
- "AC97" //AIN5
+ "CD", /* AIN1 */
+ "Aux", /* AIN2 */
+ "Line", /* AIN3 */
+ "Mic", /* AIN4 */
+ "AC97" /* AIN5 */
};
static const char * const universe_texts[] = {
- "Aux1", //AIN1
- "CD", //AIN2
- "Phono", //AIN3
- "Line", //AIN4
- "Aux2", //AIN5
- "Mic", //AIN6
- "Aux3", //AIN7
- "AC97" //AIN8
+ "Aux1", /* AIN1 */
+ "CD", /* AIN2 */
+ "Phono", /* AIN3 */
+ "Line", /* AIN4 */
+ "Aux2", /* AIN5 */
+ "Mic", /* AIN6 */
+ "Aux3", /* AIN7 */
+ "AC97" /* AIN8 */
};
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
@@ -1108,8 +1128,7 @@ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]);
- }
- else {
+ } else {
uinfo->value.enumerated.items = 5;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
@@ -1156,8 +1175,8 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
static const char * const aureon_texts[] = {
- "CD", //RXP0
- "Optical" //RXP1
+ "CD", /* RXP0 */
+ "Optical" /* RXP1 */
};
static const char * const prodigy_texts[] = {
"CD",
@@ -1180,10 +1199,10 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
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);
+ /* snd_ice1712_save_gpio_status(ice); */
+ /* val = aureon_cs8415_get(ice, CS8415_CTRL2); */
ucontrol->value.enumerated.item[0] = spec->cs8415_mux;
- //snd_ice1712_restore_gpio_status(ice);
+ /* snd_ice1712_restore_gpio_status(ice); */
return 0;
}
@@ -1206,7 +1225,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-static int aureon_cs8415_rate_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int aureon_cs8415_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
@@ -1215,7 +1234,7 @@ static int aureon_cs8415_rate_info (struct snd_kcontrol *kcontrol, struct snd_ct
return 0;
}
-static int aureon_cs8415_rate_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int aureon_cs8415_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char ratio;
@@ -1229,7 +1248,7 @@ static int aureon_cs8415_rate_get (struct snd_kcontrol *kcontrol, struct snd_ctl
*/
#define aureon_cs8415_mute_info snd_ctl_boolean_mono_info
-static int aureon_cs8415_mute_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int aureon_cs8415_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
snd_ice1712_save_gpio_status(ice);
@@ -1238,7 +1257,7 @@ static int aureon_cs8415_mute_get (struct snd_kcontrol *kcontrol, struct snd_ctl
return 0;
}
-static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int aureon_cs8415_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char oval, nval;
@@ -1249,7 +1268,8 @@ static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl
nval = oval & ~0x20;
else
nval = oval | 0x20;
- if ((change = (oval != nval)))
+ change = (oval != nval);
+ if (change)
aureon_cs8415_put(ice, CS8415_CTRL1, nval);
snd_ice1712_restore_gpio_status(ice);
return change;
@@ -1258,15 +1278,17 @@ static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl
/*
* CS8415A Q-Sub info
*/
-static int aureon_cs8415_qsub_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int aureon_cs8415_qsub_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = 10;
return 0;
}
-static int aureon_cs8415_qsub_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_cs8415_qsub_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+
snd_ice1712_save_gpio_status(ice);
aureon_cs8415_read(ice, CS8415_QSUB, ucontrol->value.bytes.data, 10);
snd_ice1712_restore_gpio_status(ice);
@@ -1274,18 +1296,21 @@ static int aureon_cs8415_qsub_get (struct snd_kcontrol *kcontrol, struct snd_ctl
return 0;
}
-static int aureon_cs8415_spdif_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int aureon_cs8415_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
-static int aureon_cs8415_mask_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_cs8415_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
memset(ucontrol->value.iec958.status, 0xFF, 24);
return 0;
}
-static int aureon_cs8415_spdif_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_cs8415_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
snd_ice1712_save_gpio_status(ice);
@@ -1311,9 +1336,9 @@ static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
else
if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT)
- tmp &= ~ AUREON_HP_SEL;
+ tmp &= ~AUREON_HP_SEL;
else
- tmp &= ~ PRODIGY_HP_SEL;
+ tmp &= ~PRODIGY_HP_SEL;
if (tmp != tmp2) {
snd_ice1712_gpio_write(ice, tmp);
return 1;
@@ -1325,7 +1350,7 @@ static int aureon_get_headphone_amp(struct snd_ice1712 *ice)
{
unsigned int tmp = snd_ice1712_gpio_read(ice);
- return ( tmp & AUREON_HP_SEL )!= 0;
+ return (tmp & AUREON_HP_SEL) != 0;
}
#define aureon_hpamp_info snd_ctl_boolean_mono_info
@@ -1343,7 +1368,7 @@ static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
+ return aureon_set_headphone_amp(ice, ucontrol->value.integer.value[0]);
}
/*
@@ -1390,7 +1415,7 @@ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return 0;
}
static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1434,7 +1459,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = wm_master_vol_info,
.get = wm_master_vol_get,
@@ -1452,7 +1477,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Front Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
@@ -1471,7 +1496,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Rear Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
@@ -1490,7 +1515,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Center Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
@@ -1509,7 +1534,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "LFE Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
@@ -1528,7 +1553,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Side Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
@@ -1539,23 +1564,23 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
};
static struct snd_kcontrol_new wm_controls[] __devinitdata = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.info = wm_pcm_mute_info,
.get = wm_pcm_mute_get,
.put = wm_pcm_mute_put
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume",
.info = wm_pcm_vol_info,
.get = wm_pcm_vol_get,
.put = wm_pcm_vol_put,
.tlv = { .p = db_scale_wm_pcm }
- },
+ },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Switch",
@@ -1566,7 +1591,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume",
.info = wm_adc_vol_info,
.get = wm_adc_vol_get,
@@ -1605,232 +1630,232 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
};
static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
.info = aureon_ac97_mmute_info,
.get = aureon_ac97_mmute_get,
.put = aureon_ac97_mmute_put,
.private_value = AC97_MASTER
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "AC97 Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_MASTER|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "AC97 Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MASTER|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_master }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "CD Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_CD
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_CD
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "CD Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_CD|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "CD Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_CD|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Aux Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_AUX,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_AUX,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Aux Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_AUX|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Aux Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_AUX|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Line Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_LINE
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_LINE
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Line Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_LINE|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Line Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_LINE|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_MIC
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_MIC
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Mic Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_MIC,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Mic Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MIC,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic Boost (+20dB)",
- .info = aureon_ac97_micboost_info,
- .get = aureon_ac97_micboost_get,
- .put = aureon_ac97_micboost_put
- }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Boost (+20dB)",
+ .info = aureon_ac97_micboost_info,
+ .get = aureon_ac97_micboost_get,
+ .put = aureon_ac97_micboost_put
+ }
};
static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
.info = aureon_ac97_mmute_info,
.get = aureon_ac97_mmute_get,
.put = aureon_ac97_mmute_put,
.private_value = AC97_MASTER
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "AC97 Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_MASTER|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "AC97 Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MASTER|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_master }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "CD Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_AUX
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_AUX
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "CD Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_AUX|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "CD Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_AUX|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Phono Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_CD
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Phono Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_CD
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Phono Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_CD|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Phono Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_CD|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Line Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_LINE
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_LINE
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Line Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_LINE|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Line Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_LINE|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_MIC
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_MIC
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Mic Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_MIC,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Mic Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_MIC,
.tlv = { .p = db_scale_ac97_gain }
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic Boost (+20dB)",
- .info = aureon_ac97_micboost_info,
- .get = aureon_ac97_micboost_get,
- .put = aureon_ac97_micboost_put
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Aux Playback Switch",
- .info = aureon_ac97_mute_info,
- .get = aureon_ac97_mute_get,
- .put = aureon_ac97_mute_put,
- .private_value = AC97_VIDEO,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Boost (+20dB)",
+ .info = aureon_ac97_micboost_info,
+ .get = aureon_ac97_micboost_get,
+ .put = aureon_ac97_micboost_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Aux Playback Switch",
+ .info = aureon_ac97_mute_info,
+ .get = aureon_ac97_mute_get,
+ .put = aureon_ac97_mute_put,
+ .private_value = AC97_VIDEO,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Aux Playback Volume",
- .info = aureon_ac97_vol_info,
- .get = aureon_ac97_vol_get,
- .put = aureon_ac97_vol_put,
- .private_value = AC97_VIDEO|AUREON_AC97_STEREO,
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Aux Playback Volume",
+ .info = aureon_ac97_vol_info,
+ .get = aureon_ac97_vol_get,
+ .put = aureon_ac97_vol_put,
+ .private_value = AC97_VIDEO|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
- },
+ },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Aux Source",
@@ -1844,43 +1869,43 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
.info = aureon_cs8415_mute_info,
.get = aureon_cs8415_mute_get,
.put = aureon_cs8415_mute_put
},
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Source",
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Source",
.info = aureon_cs8415_mux_info,
.get = aureon_cs8415_mux_get,
.put = aureon_cs8415_mux_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("Q-subcode ",CAPTURE,DEFAULT),
+ .name = SNDRV_CTL_NAME_IEC958("Q-subcode ", CAPTURE, DEFAULT),
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = aureon_cs8415_qsub_info,
.get = aureon_cs8415_qsub_get,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = aureon_cs8415_spdif_info,
.get = aureon_cs8415_mask_get
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = aureon_cs8415_spdif_info,
.get = aureon_cs8415_spdif_get
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Rate",
- .access =SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = aureon_cs8415_rate_info,
.get = aureon_cs8415_rate_get
}
@@ -1905,15 +1930,14 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
}
-
+
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
for (i = 0; i < ARRAY_SIZE(universe_ac97_controls); i++) {
err = snd_ctl_add(ice->card, snd_ctl_new1(&universe_ac97_controls[i], ice));
if (err < 0)
return err;
}
- }
- else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+ } else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) {
err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice));
@@ -1932,7 +1956,7 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
else if ((id & 0x0F) != 0x01)
snd_printk(KERN_INFO "Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));
else {
- for (i = 0; i< ARRAY_SIZE(cs8415_controls); i++) {
+ for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
struct snd_kcontrol *kctl;
err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));
if (err < 0)
@@ -1943,7 +1967,7 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
}
snd_ice1712_restore_gpio_status(ice);
}
-
+
return 0;
}
@@ -2059,11 +2083,12 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
/* to remeber the register values of CS8415 */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
- if (! ice->akm)
+ if (!ice->akm)
return -ENOMEM;
ice->akm_codecs = 1;
-
- if ((err = aureon_ac97_init(ice)) != 0)
+
+ err = aureon_ac97_init(ice);
+ if (err != 0)
return err;
snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
@@ -2086,7 +2111,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
/* initialize WM8770 codec */
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71 ||
ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
- ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)
+ ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)
p = wm_inits_prodigy;
else
p = wm_inits_aureon;
@@ -2105,10 +2130,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
snd_ice1712_restore_gpio_status(ice);
- /* initialize PCA9554 pin directions & set default input*/
+ /* initialize PCA9554 pin directions & set default input */
aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */
-
+
spec->master[0] = WM_VOL_MUTE;
spec->master[1] = WM_VOL_MUTE;
for (i = 0; i < ice->num_total_dacs; i++) {
@@ -2158,6 +2183,24 @@ static unsigned char aureon71_eeprom[] __devinitdata = {
};
#define prodigy71_eeprom aureon71_eeprom
+static unsigned char aureon71_universe_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC,
+ * 4DACs
+ */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+ [ICE_EEP2_GPIO_DIR] = 0xff,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x5f,
+ [ICE_EEP2_GPIO_MASK] = 0x00,
+ [ICE_EEP2_GPIO_MASK1] = 0x00,
+ [ICE_EEP2_GPIO_MASK2] = 0x00,
+ [ICE_EEP2_GPIO_STATE] = 0x00,
+ [ICE_EEP2_GPIO_STATE1] = 0x00,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
+};
+
static unsigned char prodigy71lt_eeprom[] __devinitdata = {
[ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -2197,14 +2240,14 @@ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
.eeprom_data = aureon71_eeprom,
.driver = "Aureon71",
},
- {
- .subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
- .name = "Terratec Aureon 7.1-Universe",
+ {
+ .subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
+ .name = "Terratec Aureon 7.1-Universe",
.model = "universe",
- .chip_init = aureon_init,
- .build_controls = aureon_add_controls,
- .eeprom_size = sizeof(aureon71_eeprom),
- .eeprom_data = aureon71_eeprom,
+ .chip_init = aureon_init,
+ .build_controls = aureon_add_controls,
+ .eeprom_size = sizeof(aureon71_universe_eeprom),
+ .eeprom_data = aureon71_universe_eeprom,
.driver = "Aureon71Univ", /* keep in 15 letters */
},
{
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 0ed96c17805..d216362626d 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -400,7 +400,7 @@ static void delta_setup_spdif(struct snd_ice1712 *ice, int rate)
static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- char reg = 0x10; // cs8427 receiver error register
+ char reg = 0x10; /* CS8427 receiver error register */
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index ea7116c304c..f7f14df81f2 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -31,6 +31,7 @@
"{MidiMan M Audio,Delta DiO 2496},"\
"{MidiMan M Audio,Delta 66},"\
"{MidiMan M Audio,Delta 44},"\
+ "{MidiMan M Audio,Delta 410},"\
"{MidiMan M Audio,Audiophile 24/96},"\
"{Digigram,VX442},"\
"{Lionstracs,Mediastation},"
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
index 43b9e3e858b..a0c5e009bb4 100644
--- a/sound/pci/ice1712/envy24ht.h
+++ b/sound/pci/ice1712/envy24ht.h
@@ -93,9 +93,13 @@ enum {
#define VT1724_REG_MPU_TXFIFO 0x0a /*byte ro. number of bytes in TX fifo*/
#define VT1724_REG_MPU_RXFIFO 0x0b /*byte ro. number of bytes in RX fifo*/
-//are these 2 the wrong way around? they don't seem to be used yet anyway
-#define VT1724_REG_MPU_CTRL 0x0c /* byte */
-#define VT1724_REG_MPU_DATA 0x0d /* byte */
+#define VT1724_REG_MPU_DATA 0x0c /* byte */
+#define VT1724_REG_MPU_CTRL 0x0d /* byte */
+#define VT1724_MPU_UART 0x01
+#define VT1724_MPU_TX_EMPTY 0x02
+#define VT1724_MPU_TX_FULL 0x04
+#define VT1724_MPU_RX_EMPTY 0x08
+#define VT1724_MPU_RX_FULL 0x10
#define VT1724_REG_MPU_FIFO_WM 0x0e /*byte set the high/low watermarks for RX/TX fifos*/
#define VT1724_MPU_RX_FIFO 0x20 //1=rx fifo watermark 0=tx fifo watermark
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 013fc4f0482..6fe35b81204 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -149,7 +149,8 @@ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mas
struct ews_spec *spec = ice->spec;
unsigned char data, ndata;
- snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
+ if (snd_BUG_ON(chip_mask < 0 || chip_mask > 0x0f))
+ return -EINVAL;
snd_i2c_lock(ice->i2c);
if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
goto __error;
@@ -685,7 +686,8 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st
int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned char data;
- snd_assert(channel >= 0 && channel <= 7, return 0);
+ if (snd_BUG_ON(channel < 0 || channel > 7))
+ return 0;
snd_i2c_lock(ice->i2c);
if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
snd_i2c_unlock(ice->i2c);
@@ -705,7 +707,8 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st
int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned char data, ndata;
- snd_assert(channel >= 0 && channel <= 7, return 0);
+ if (snd_BUG_ON(channel < 0 || channel > 7))
+ return 0;
snd_i2c_lock(ice->i2c);
if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
snd_i2c_unlock(ice->i2c);
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 29d449d73c9..5b442383fcd 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
/*
NOTES:
@@ -35,7 +35,7 @@
*
* 2002.11.26 James Stafford <jstafford@ampltd.com>
* Added support for VT1724 (Envy24HT)
- * I have left out support for 176.4 and 192 KHz for the moment.
+ * I have left out support for 176.4 and 192 KHz for the moment.
* I also haven't done anything with the internal S/PDIF transmitter or the MPU-401
*
* 2003.02.20 Taksahi Iwai <tiwai@suse.de>
@@ -47,7 +47,7 @@
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -123,7 +123,7 @@ static unsigned int PRO_RATE_DEFAULT = 44100;
/*
* Basic I/O
*/
-
+
/* check whether the clock mode is spdif-in */
static inline int is_spdif_master(struct snd_ice1712 *ice)
{
@@ -135,13 +135,13 @@ static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
return is_spdif_master(ice) || PRO_RATE_LOCKED;
}
-static inline void snd_ice1712_ds_write(struct snd_ice1712 * ice, u8 channel, u8 addr, u32 data)
+static inline void snd_ice1712_ds_write(struct snd_ice1712 *ice, u8 channel, u8 addr, u32 data)
{
outb((channel << 4) | addr, ICEDS(ice, INDEX));
outl(data, ICEDS(ice, DATA));
}
-static inline u32 snd_ice1712_ds_read(struct snd_ice1712 * ice, u8 channel, u8 addr)
+static inline u32 snd_ice1712_ds_read(struct snd_ice1712 *ice, u8 channel, u8 addr)
{
outb((channel << 4) | addr, ICEDS(ice, INDEX));
return inl(ICEDS(ice, DATA));
@@ -260,7 +260,7 @@ static unsigned short snd_ice1712_pro_ac97_read(struct snd_ac97 *ac97,
static int snd_ice1712_digmix_route_ac97_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
return 0;
}
@@ -269,11 +269,12 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val, nval;
-
+
spin_lock_irq(&ice->reg_lock);
val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
nval = val & ~ICE1712_ROUTE_AC97;
- if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+ if (ucontrol->value.integer.value[0])
+ nval |= ICE1712_ROUTE_AC97;
outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
spin_unlock_irq(&ice->reg_lock);
return val != nval;
@@ -329,7 +330,7 @@ static int snd_ice1712_cs8427_set_input_clock(struct snd_ice1712 *ice, int spdif
unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */
unsigned char val, nval;
int res = 0;
-
+
snd_i2c_lock(ice->i2c);
if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
snd_i2c_unlock(ice->i2c);
@@ -381,9 +382,9 @@ int __devinit snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
{
int err;
- if ((err = snd_cs8427_create(ice->i2c, addr,
- (ice->cs8427_timeout * HZ) / 1000,
- &ice->cs8427)) < 0) {
+ err = snd_cs8427_create(ice->i2c, addr,
+ (ice->cs8427_timeout * HZ) / 1000, &ice->cs8427);
+ if (err < 0) {
snd_printk(KERN_ERR "CS8427 initialization failed\n");
return err;
}
@@ -395,9 +396,9 @@ int __devinit snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
static void snd_ice1712_set_input_clock_source(struct snd_ice1712 *ice, int spdif_is_master)
{
- /* change CS8427 clock source too */
- if (ice->cs8427)
- snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master);
+ /* change CS8427 clock source too */
+ if (ice->cs8427)
+ snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master);
/* notify ak4524 chip as well */
if (spdif_is_master) {
unsigned int i;
@@ -457,11 +458,12 @@ static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)
u16 pbkstatus;
struct snd_pcm_substream *substream;
pbkstatus = inw(ICEDS(ice, INTSTAT));
- //printk("pbkstatus = 0x%x\n", pbkstatus);
+ /* printk("pbkstatus = 0x%x\n", pbkstatus); */
for (idx = 0; idx < 6; idx++) {
if ((pbkstatus & (3 << (idx * 2))) == 0)
continue;
- if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+ substream = ice->playback_con_substream_ds[idx];
+ if (substream != NULL)
snd_pcm_period_elapsed(substream);
outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
}
@@ -507,7 +509,7 @@ static int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
int result = 0;
u32 tmp;
-
+
spin_lock(&ice->reg_lock);
tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
@@ -532,7 +534,7 @@ static int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
int result = 0;
u32 tmp;
-
+
spin_lock(&ice->reg_lock);
tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
@@ -557,7 +559,7 @@ static int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
int result = 0;
u8 tmp;
-
+
spin_lock(&ice->reg_lock);
tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
@@ -711,8 +713,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *s
return bytes_to_frames(substream->runtime, ptr);
}
-static const struct snd_pcm_hardware snd_ice1712_playback =
-{
+static const struct snd_pcm_hardware snd_ice1712_playback = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -731,8 +732,7 @@ static const struct snd_pcm_hardware snd_ice1712_playback =
.fifo_size = 0,
};
-static const struct snd_pcm_hardware snd_ice1712_playback_ds =
-{
+static const struct snd_pcm_hardware snd_ice1712_playback_ds = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -751,8 +751,7 @@ static const struct snd_pcm_hardware snd_ice1712_playback_ds =
.fifo_size = 0,
};
-static const struct snd_pcm_hardware snd_ice1712_capture =
-{
+static const struct snd_pcm_hardware snd_ice1712_capture = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -788,7 +787,7 @@ static int snd_ice1712_playback_ds_open(struct snd_pcm_substream *substream)
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_ice1712_playback_ds;
- spin_lock_irq(&ice->reg_lock);
+ spin_lock_irq(&ice->reg_lock);
tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
outw(tmp, ICEDS(ice, INTMASK));
spin_unlock_irq(&ice->reg_lock);
@@ -821,7 +820,7 @@ static int snd_ice1712_playback_ds_close(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
u32 tmp;
- spin_lock_irq(&ice->reg_lock);
+ spin_lock_irq(&ice->reg_lock);
tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
outw(tmp, ICEDS(ice, INTMASK));
spin_unlock_irq(&ice->reg_lock);
@@ -870,7 +869,7 @@ static struct snd_pcm_ops snd_ice1712_capture_ops = {
.pointer = snd_ice1712_capture_pointer,
};
-static int __devinit snd_ice1712_pcm(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
@@ -900,7 +899,7 @@ static int __devinit snd_ice1712_pcm(struct snd_ice1712 * ice, int device, struc
return 0;
}
-static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
@@ -1029,14 +1028,14 @@ static void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
ICE1712_PLAYBACK_PAUSE|
ICE1712_PLAYBACK_START)) {
- __out:
+__out:
spin_unlock_irqrestore(&ice->reg_lock, flags);
return;
}
if (!force && is_pro_rate_locked(ice))
goto __out;
- old = inb(ICEMT(ice, RATE));
+ old = inb(ICEMT(ice, RATE));
if (!force && old == val)
goto __out;
outb(val, ICEMT(ice, RATE));
@@ -1123,8 +1122,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substrea
return bytes_to_frames(substream->runtime, ptr);
}
-static const struct snd_pcm_hardware snd_ice1712_playback_pro =
-{
+static const struct snd_pcm_hardware snd_ice1712_playback_pro = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1143,8 +1141,7 @@ static const struct snd_pcm_hardware snd_ice1712_playback_pro =
.fifo_size = 0,
};
-static const struct snd_pcm_hardware snd_ice1712_capture_pro =
-{
+static const struct snd_pcm_hardware snd_ice1712_capture_pro = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1238,7 +1235,7 @@ static struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
.pointer = snd_ice1712_capture_pro_pointer,
};
-static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
@@ -1262,7 +1259,7 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device,
ice->pcm_pro = pcm;
if (rpcm)
*rpcm = pcm;
-
+
if (ice->cs8427) {
/* assign channels to iec958 */
err = snd_cs8427_iec958_build(ice->cs8427,
@@ -1272,7 +1269,8 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device,
return err;
}
- if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
+ err = snd_ice1712_build_pro_mixer(ice);
+ if (err < 0)
return err;
return 0;
}
@@ -1299,7 +1297,7 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
kcontrol->private_value;
-
+
spin_lock_irq(&ice->reg_lock);
ucontrol->value.integer.value[0] =
!((ice->pro_volumes[priv_idx] >> 15) & 1);
@@ -1341,7 +1339,7 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
kcontrol->private_value;
-
+
spin_lock_irq(&ice->reg_lock);
ucontrol->value.integer.value[0] =
(ice->pro_volumes[priv_idx] >> 0) & 127;
@@ -1406,7 +1404,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinit
static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,SWITCH),
+ .name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
.info = snd_ice1712_pro_mixer_switch_info,
.get = snd_ice1712_pro_mixer_switch_get,
.put = snd_ice1712_pro_mixer_switch_put,
@@ -1428,7 +1426,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinit
static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,VOLUME),
+ .name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
.info = snd_ice1712_pro_mixer_volume_info,
.get = snd_ice1712_pro_mixer_volume_get,
.put = snd_ice1712_pro_mixer_volume_put,
@@ -1448,7 +1446,7 @@ static int __devinit snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)
if (err < 0)
return err;
}
-
+
if (ice->num_total_adcs > 0) {
struct snd_kcontrol_new tmp = snd_ice1712_multi_capture_analog_switch;
tmp.count = ice->num_total_adcs;
@@ -1495,7 +1493,7 @@ static void snd_ice1712_mixer_free_ac97(struct snd_ac97 *ac97)
ice->ac97 = NULL;
}
-static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 * ice)
+static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
{
int err, bus_num = 0;
struct snd_ac97_template ac97;
@@ -1510,27 +1508,32 @@ static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 * ice)
};
if (ice_has_con_ac97(ice)) {
- if ((err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = ice;
ac97.private_free = snd_ice1712_mixer_free_ac97;
- if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
+ if (err < 0)
printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
else {
- if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice));
+ if (err < 0)
return err;
return 0;
}
}
- if (! (ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) {
- if ((err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus)) < 0)
+ if (!(ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) {
+ err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = ice;
ac97.private_free = snd_ice1712_mixer_free_ac97;
- if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
+ if (err < 0)
printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
else
return 0;
@@ -1549,7 +1552,7 @@ static inline unsigned int eeprom_double(struct snd_ice1712 *ice, int idx)
return (unsigned int)ice->eeprom.data[idx] | ((unsigned int)ice->eeprom.data[idx + 1] << 8);
}
-static void snd_ice1712_proc_read(struct snd_info_entry *entry,
+static void snd_ice1712_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ice1712 *ice = entry->private_data;
@@ -1585,15 +1588,15 @@ static void snd_ice1712_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " SPDOUT : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT)));
snd_iprintf(buffer, " RATE : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE)));
snd_iprintf(buffer, " GPIO_DATA : 0x%02x\n", (unsigned)snd_ice1712_get_gpio_data(ice));
- snd_iprintf(buffer, " GPIO_WRITE_MASK : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK));
+ snd_iprintf(buffer, " GPIO_WRITE_MASK : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK));
snd_iprintf(buffer, " GPIO_DIRECTION : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION));
}
-static void __devinit snd_ice1712_proc_init(struct snd_ice1712 * ice)
+static void __devinit snd_ice1712_proc_init(struct snd_ice1712 *ice)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "ice1712", &entry))
+ if (!snd_card_proc_new(ice->card, "ice1712", &entry))
snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read);
}
@@ -1613,7 +1616,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+
memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
return 0;
}
@@ -1641,7 +1644,7 @@ static int snd_ice1712_spdif_default_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
if (ice->spdif.ops.default_get)
- ice->spdif.ops.default_get(ice, ucontrol);
+ ice->spdif.ops.default_get(ice, ucontrol);
return 0;
}
@@ -1657,7 +1660,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.info = snd_ice1712_spdif_info,
.get = snd_ice1712_spdif_default_get,
.put = snd_ice1712_spdif_default_put
@@ -1709,7 +1712,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
.info = snd_ice1712_spdif_info,
.get = snd_ice1712_spdif_maskc_get,
};
@@ -1718,7 +1721,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
.info = snd_ice1712_spdif_info,
.get = snd_ice1712_spdif_maskp_get,
};
@@ -1746,7 +1749,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata =
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
.info = snd_ice1712_spdif_info,
.get = snd_ice1712_spdif_stream_get,
.put = snd_ice1712_spdif_stream_put
@@ -1758,7 +1761,7 @@ int snd_ice1712_gpio_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char mask = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
-
+
snd_ice1712_save_gpio_status(ice);
ucontrol->value.integer.value[0] =
(snd_ice1712_gpio_read(ice) & mask ? 1 : 0) ^ invert;
@@ -1825,7 +1828,7 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10
};
unsigned char val;
-
+
spin_lock_irq(&ice->reg_lock);
if (is_spdif_master(ice)) {
ucontrol->value.enumerated.item[0] = 13;
@@ -1867,7 +1870,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
if ((oval & ICE1712_SPDIF_MASTER) !=
(inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER))
- snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice));
+ snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice));
return change;
}
@@ -1897,7 +1900,7 @@ static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcon
"64000", /* 10: 15 */
"88200", /* 11: 11 */
"96000", /* 12: 7 */
- // "IEC958 Input", /* 13: -- */
+ /* "IEC958 Input", 13: -- */
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
@@ -2026,7 +2029,7 @@ static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol,
"IEC958 In L", "IEC958 In R", /* 9-10 */
"Digital Mixer", /* 11 - optional */
};
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items =
@@ -2070,7 +2073,7 @@ static int snd_ice1712_pro_route_analog_put(struct snd_kcontrol *kcontrol,
int change, shift;
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int val, old_val, nval;
-
+
/* update PSDOUT */
if (ucontrol->value.enumerated.item[0] >= 11)
nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
@@ -2140,7 +2143,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
int change, shift;
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int val, old_val, nval;
-
+
/* update SPDOUT */
spin_lock_irq(&ice->reg_lock);
val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
@@ -2182,7 +2185,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata
static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_ice1712_pro_route_info,
.get = snd_ice1712_pro_route_spdif_get,
.put = snd_ice1712_pro_route_spdif_put,
@@ -2204,7 +2207,7 @@ static int snd_ice1712_pro_volume_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
return 0;
}
@@ -2245,7 +2248,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
-
+
spin_lock_irq(&ice->reg_lock);
for (idx = 0; idx < 22; idx++) {
outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
@@ -2296,12 +2299,12 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
unsigned int i, size;
struct snd_ice1712_card_info * const *tbl, *c;
- if (! modelname || ! *modelname) {
+ if (!modelname || !*modelname) {
ice->eeprom.subvendor = 0;
if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) != 0)
ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
- (snd_ice1712_read_i2c(ice, dev, 0x01) << 8) |
- (snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |
+ (snd_ice1712_read_i2c(ice, dev, 0x01) << 8) |
+ (snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |
(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
if (ice->eeprom.subvendor == 0 ||
ice->eeprom.subvendor == (unsigned int)-1) {
@@ -2318,12 +2321,12 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
}
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
- if (modelname && c->model && ! strcmp(modelname, c->model)) {
+ if (modelname && c->model && !strcmp(modelname, c->model)) {
printk(KERN_INFO "ice1712: Using board model %s\n", c->name);
ice->eeprom.subvendor = c->subvendor;
} else if (c->subvendor != ice->eeprom.subvendor)
continue;
- if (! c->eeprom_size || ! c->eeprom_data)
+ if (!c->eeprom_size || !c->eeprom_data)
goto found;
/* if the EEPROM is given by the driver, use it */
snd_printdd("using the defined eeprom..\n");
@@ -2416,7 +2419,8 @@ int __devinit snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
int err;
struct snd_kcontrol *kctl;
- snd_assert(ice->pcm_pro != NULL, return -EIO);
+ if (snd_BUG_ON(!ice->pcm_pro))
+ return -EIO;
err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
if (err < 0)
return err;
@@ -2483,13 +2487,13 @@ static int __devinit snd_ice1712_build_controls(struct snd_ice1712 *ice)
static int snd_ice1712_free(struct snd_ice1712 *ice)
{
- if (! ice->port)
+ if (!ice->port)
goto __hw_end;
/* mask all interrupts */
outb(0xc0, ICEMT(ice, IRQ));
outb(0xff, ICEREG(ice, IRQMASK));
/* --- */
- __hw_end:
+__hw_end:
if (ice->irq >= 0)
free_irq(ice->irq, ice);
@@ -2514,7 +2518,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
int omni,
int cs8427_timeout,
int dxr_enable,
- struct snd_ice1712 ** r_ice1712)
+ struct snd_ice1712 **r_ice1712)
{
struct snd_ice1712 *ice;
int err;
@@ -2524,8 +2528,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
*r_ice1712 = NULL;
- /* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ /* enable PCI device */
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 28 bits */
if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
@@ -2569,7 +2574,8 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
snd_ice1712_proc_init(ice);
synchronize_irq(pci->irq);
- if ((err = pci_request_regions(pci, "ICE1712")) < 0) {
+ err = pci_request_regions(pci, "ICE1712");
+ if (err < 0) {
kfree(ice);
pci_disable_device(pci);
return err;
@@ -2585,7 +2591,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
snd_ice1712_free(ice);
return -EIO;
}
-
+
ice->irq = pci->irq;
if (snd_ice1712_read_eeprom(ice, modelname) < 0) {
@@ -2605,9 +2611,10 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
ICEREG(ice, IRQMASK));
outb(0x00, ICEMT(ice, IRQ));
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
+ if (err < 0) {
snd_ice1712_free(ice);
- return err;
+ return err;
}
snd_card_set_dev(card, &pci->dev);
@@ -2647,10 +2654,10 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
strcpy(card->driver, "ICE1712");
strcpy(card->shortname, "ICEnsemble ICE1712");
-
- if ((err = snd_ice1712_create(card, pci, model[dev], omni[dev],
- cs8427_timeout[dev], dxr_enable[dev],
- &ice)) < 0) {
+
+ err = snd_ice1712_create(card, pci, model[dev], omni[dev],
+ cs8427_timeout[dev], dxr_enable[dev], &ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -2662,7 +2669,8 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
if (c->driver) /* specific driver? */
strcpy(card->driver, c->driver);
if (c->chip_init) {
- if ((err = c->chip_init(ice)) < 0) {
+ err = c->chip_init(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -2674,47 +2682,52 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
c = &no_matched;
__found:
- if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
+ err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL);
+ if (err < 0) {
snd_card_free(card);
return err;
}
-
+
if (ice_has_con_ac97(ice))
- if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
+ err = snd_ice1712_pcm(ice, pcm_dev++, NULL);
+ if (err < 0) {
snd_card_free(card);
return err;
}
- if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
+ err = snd_ice1712_ac97_mixer(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
- if ((err = snd_ice1712_build_controls(ice)) < 0) {
+ err = snd_ice1712_build_controls(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
if (c->build_controls) {
- if ((err = c->build_controls(ice)) < 0) {
+ err = c->build_controls(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
}
if (ice_has_con_ac97(ice))
- if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
+ err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL);
+ if (err < 0) {
snd_card_free(card);
return err;
}
- if (! c->no_mpu401) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
- ICEREG(ice, MPU1_CTRL),
- (c->mpu401_1_info_flags |
- MPU401_INFO_INTEGRATED),
- ice->irq, 0,
- &ice->rmidi[0])) < 0) {
+ if (!c->no_mpu401) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+ ICEREG(ice, MPU1_CTRL),
+ (c->mpu401_1_info_flags | MPU401_INFO_INTEGRATED),
+ ice->irq, 0, &ice->rmidi[0]);
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -2726,12 +2739,12 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {
/* 2nd port used */
- if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
- ICEREG(ice, MPU2_CTRL),
- (c->mpu401_2_info_flags |
- MPU401_INFO_INTEGRATED),
- ice->irq, 0,
- &ice->rmidi[1])) < 0) {
+ err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
+ ICEREG(ice, MPU2_CTRL),
+ (c->mpu401_2_info_flags | MPU401_INFO_INTEGRATED),
+ ice->irq, 0, &ice->rmidi[1]);
+
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -2749,7 +2762,8 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, ice->port, ice->irq);
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_card_free(card);
return err;
}
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 3208901c740..fdae6deba16 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -20,7 +20,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
#include <sound/control.h>
#include <sound/ac97_codec.h>
@@ -112,7 +112,7 @@
*/
#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
-
+
#define ICE1712_DS_INTMASK 0x00 /* word - interrupt mask */
#define ICE1712_DS_INTSTAT 0x02 /* word - interrupt status */
#define ICE1712_DS_DATA 0x04 /* dword - channel data */
@@ -121,7 +121,7 @@
/*
* Consumer section channel registers
*/
-
+
#define ICE1712_DSC_ADDR0 0x00 /* dword - base address 0 */
#define ICE1712_DSC_COUNT0 0x01 /* word - count 0 */
#define ICE1712_DSC_ADDR1 0x02 /* dword - base address 1 */
@@ -138,7 +138,7 @@
#define ICE1712_DSC_RATE 0x05 /* dword - rate */
#define ICE1712_DSC_VOLUME 0x06 /* word - volume control */
-/*
+/*
* Professional multi-track direct control registers
*/
@@ -214,7 +214,7 @@
/*
- *
+ *
*/
struct snd_ice1712;
@@ -253,12 +253,12 @@ enum {
ICE_EEP1_ADC_ID2,
ICE_EEP1_ADC_ID3
};
-
+
#define ice_has_con_ac97(ice) (!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97))
struct snd_ak4xxx_private {
- unsigned int cif: 1; /* CIF mode */
+ unsigned int cif:1; /* CIF mode */
unsigned char caddr; /* C0 and C1 bits */
unsigned int data_mask; /* DATA gpio bit */
unsigned int clk_mask; /* CLK gpio bit */
@@ -306,11 +306,11 @@ struct snd_ice1712 {
struct snd_pcm *pcm;
struct snd_pcm *pcm_ds;
struct snd_pcm *pcm_pro;
- struct snd_pcm_substream *playback_con_substream;
- struct snd_pcm_substream *playback_con_substream_ds[6];
- struct snd_pcm_substream *capture_con_substream;
- struct snd_pcm_substream *playback_pro_substream;
- struct snd_pcm_substream *capture_pro_substream;
+ struct snd_pcm_substream *playback_con_substream;
+ struct snd_pcm_substream *playback_con_substream_ds[6];
+ struct snd_pcm_substream *capture_con_substream;
+ struct snd_pcm_substream *playback_pro_substream;
+ struct snd_pcm_substream *capture_pro_substream;
unsigned int playback_pro_size;
unsigned int capture_pro_size;
unsigned int playback_con_virt_addr[6];
@@ -326,13 +326,15 @@ struct snd_ice1712 {
struct snd_ice1712_eeprom eeprom;
unsigned int pro_volumes[20];
- unsigned int omni: 1; /* Delta Omni I/O */
- unsigned int dxr_enable: 1; /* Terratec DXR enable for DMX6FIRE */
- unsigned int vt1724: 1;
- unsigned int vt1720: 1;
- unsigned int has_spdif: 1; /* VT1720/4 - has SPDIF I/O */
- unsigned int force_pdma4: 1; /* VT1720/4 - PDMA4 as non-spdif */
- unsigned int force_rdma1: 1; /* VT1720/4 - RDMA1 as non-spdif */
+ unsigned int omni:1; /* Delta Omni I/O */
+ unsigned int dxr_enable:1; /* Terratec DXR enable for DMX6FIRE */
+ unsigned int vt1724:1;
+ unsigned int vt1720:1;
+ unsigned int has_spdif:1; /* VT1720/4 - has SPDIF I/O */
+ unsigned int force_pdma4:1; /* VT1720/4 - PDMA4 as non-spdif */
+ unsigned int force_rdma1:1; /* VT1720/4 - RDMA1 as non-spdif */
+ unsigned int midi_output:1; /* VT1720/4: MIDI output triggered */
+ unsigned int midi_input:1; /* VT1720/4: MIDI input triggered */
unsigned int num_total_dacs; /* total DACs */
unsigned int num_total_adcs; /* total ADCs */
unsigned int cur_rate; /* current rate */
@@ -349,7 +351,7 @@ struct snd_ice1712 {
struct snd_i2c_bus *i2c; /* I2C bus */
struct snd_i2c_device *cs8427; /* CS8427 I2C device */
unsigned int cs8427_timeout; /* CS8427 reset timeout in HZ/100 */
-
+
struct ice1712_gpio {
unsigned int direction; /* current direction bits */
unsigned int write_mask; /* current mask bits */
@@ -453,7 +455,7 @@ static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
{
ice->gpio.direction &= ~mask;
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
- return (snd_ice1712_gpio_read(ice) & mask);
+ return snd_ice1712_gpio_read(ice) & mask;
}
int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
@@ -465,13 +467,13 @@ int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);
int snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr);
-static inline void snd_ice1712_write(struct snd_ice1712 * ice, u8 addr, u8 data)
+static inline void snd_ice1712_write(struct snd_ice1712 *ice, u8 addr, u8 data)
{
outb(addr, ICEREG(ice, INDEX));
outb(data, ICEREG(ice, DATA));
}
-static inline u8 snd_ice1712_read(struct snd_ice1712 * ice, u8 addr)
+static inline u8 snd_ice1712_read(struct snd_ice1712 *ice, u8 addr)
{
outb(addr, ICEREG(ice, INDEX));
return inb(ICEREG(ice, DATA));
@@ -489,7 +491,7 @@ struct snd_ice1712_card_info {
char *driver;
int (*chip_init)(struct snd_ice1712 *);
int (*build_controls)(struct snd_ice1712 *);
- unsigned int no_mpu401: 1;
+ unsigned int no_mpu401:1;
unsigned int mpu401_1_info_flags;
unsigned int mpu401_2_info_flags;
const char *mpu401_1_name;
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 67350901772..1b3f1170271 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -20,9 +20,9 @@
* 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/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -32,7 +32,7 @@
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
-#include <sound/mpu401.h>
+#include <sound/rawmidi.h>
#include <sound/initval.h>
#include <sound/asoundef.h>
@@ -105,7 +105,7 @@ static unsigned int PRO_RATE_DEFAULT = 44100;
/*
* Basic I/O
*/
-
+
/*
* default rates, default clock routines
*/
@@ -198,7 +198,7 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
{
outw(data, ICEREG1724(ice, GPIO_WRITE_MASK));
- if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */
+ if (!ice->vt1720) /* VT1720 supports only 16 GPIO bits */
outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22));
inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
}
@@ -206,7 +206,7 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
{
outw(data, ICEREG1724(ice, GPIO_DATA));
- if (! ice->vt1720)
+ if (!ice->vt1720)
outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22));
inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */
}
@@ -214,7 +214,7 @@ static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
{
unsigned int data;
- if (! ice->vt1720)
+ if (!ice->vt1720)
data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22));
else
data = 0;
@@ -223,30 +223,153 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
}
/*
- * MPU401 accessor
+ * MIDI
*/
-static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
- unsigned long addr)
+
+static void vt1724_midi_clear_rx(struct snd_ice1712 *ice)
{
- /* fix status bits to the standard position */
- /* only RX_EMPTY and TX_FULL are checked */
- if (addr == MPU401C(mpu))
- return (inb(addr) & 0x0c) << 4;
+ unsigned int count;
+
+ for (count = inb(ICEREG1724(ice, MPU_RXFIFO)); count > 0; --count)
+ inb(ICEREG1724(ice, MPU_DATA));
+}
+
+static inline struct snd_rawmidi_substream *
+get_rawmidi_substream(struct snd_ice1712 *ice, unsigned int stream)
+{
+ return list_first_entry(&ice->rmidi[0]->streams[stream].substreams,
+ struct snd_rawmidi_substream, list);
+}
+
+static void vt1724_midi_write(struct snd_ice1712 *ice)
+{
+ struct snd_rawmidi_substream *s;
+ int count, i;
+ u8 buffer[32];
+
+ s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_OUTPUT);
+ count = 31 - inb(ICEREG1724(ice, MPU_TXFIFO));
+ if (count > 0) {
+ count = snd_rawmidi_transmit(s, buffer, count);
+ for (i = 0; i < count; ++i)
+ outb(buffer[i], ICEREG1724(ice, MPU_DATA));
+ }
+}
+
+static void vt1724_midi_read(struct snd_ice1712 *ice)
+{
+ struct snd_rawmidi_substream *s;
+ int count, i;
+ u8 buffer[32];
+
+ s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_INPUT);
+ count = inb(ICEREG1724(ice, MPU_RXFIFO));
+ if (count > 0) {
+ count = min(count, 32);
+ for (i = 0; i < count; ++i)
+ buffer[i] = inb(ICEREG1724(ice, MPU_DATA));
+ snd_rawmidi_receive(s, buffer, count);
+ }
+}
+
+static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
+ u8 flag, int enable)
+{
+ struct snd_ice1712 *ice = substream->rmidi->private_data;
+ u8 mask;
+
+ spin_lock_irq(&ice->reg_lock);
+ mask = inb(ICEREG1724(ice, IRQMASK));
+ if (enable)
+ mask &= ~flag;
else
- return inb(addr);
+ mask |= flag;
+ outb(mask, ICEREG1724(ice, IRQMASK));
+ spin_unlock_irq(&ice->reg_lock);
}
-static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
- unsigned char data, unsigned long addr)
+static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
{
- if (addr == MPU401C(mpu)) {
- if (data == MPU401_ENTER_UART)
- outb(0x01, addr);
- /* what else? */
- } else
- outb(data, addr);
+ vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 1);
+ return 0;
}
+static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
+{
+ vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
+ return 0;
+}
+
+static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
+{
+ struct snd_ice1712 *ice = s->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ if (up) {
+ ice->midi_output = 1;
+ vt1724_midi_write(ice);
+ } else {
+ ice->midi_output = 0;
+ }
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
+{
+ struct snd_ice1712 *ice = s->rmidi->private_data;
+ unsigned long timeout;
+
+ /* 32 bytes should be transmitted in less than about 12 ms */
+ timeout = jiffies + msecs_to_jiffies(15);
+ do {
+ if (inb(ICEREG1724(ice, MPU_CTRL)) & VT1724_MPU_TX_EMPTY)
+ break;
+ schedule_timeout_uninterruptible(1);
+ } while (time_after(timeout, jiffies));
+}
+
+static struct snd_rawmidi_ops vt1724_midi_output_ops = {
+ .open = vt1724_midi_output_open,
+ .close = vt1724_midi_output_close,
+ .trigger = vt1724_midi_output_trigger,
+ .drain = vt1724_midi_output_drain,
+};
+
+static int vt1724_midi_input_open(struct snd_rawmidi_substream *s)
+{
+ vt1724_midi_clear_rx(s->rmidi->private_data);
+ vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 1);
+ return 0;
+}
+
+static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
+{
+ vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 0);
+ return 0;
+}
+
+static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
+{
+ struct snd_ice1712 *ice = s->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ if (up) {
+ ice->midi_input = 1;
+ vt1724_midi_read(ice);
+ } else {
+ ice->midi_input = 0;
+ }
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops vt1724_midi_input_ops = {
+ .open = vt1724_midi_input_open,
+ .close = vt1724_midi_input_close,
+ .trigger = vt1724_midi_input_trigger,
+};
+
/*
* Interrupt handler
@@ -276,15 +399,12 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
break;
}
#endif
- handled = 1;
+ handled = 1;
if (status & VT1724_IRQ_MPU_TX) {
- if (ice->rmidi[0])
- snd_mpu401_uart_interrupt_tx(irq,
- ice->rmidi[0]->private_data);
- else /* disable TX to be sure */
- outb(inb(ICEREG1724(ice, IRQMASK)) |
- VT1724_IRQ_MPU_TX,
- ICEREG1724(ice, IRQMASK));
+ spin_lock(&ice->reg_lock);
+ if (ice->midi_output)
+ vt1724_midi_write(ice);
+ spin_unlock(&ice->reg_lock);
/* Due to mysterical reasons, MPU_TX is always
* generated (and can't be cleared) when a PCM
* playback is going. So let's ignore at the
@@ -293,13 +413,12 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
status_mask &= ~VT1724_IRQ_MPU_TX;
}
if (status & VT1724_IRQ_MPU_RX) {
- if (ice->rmidi[0])
- snd_mpu401_uart_interrupt(irq,
- ice->rmidi[0]->private_data);
- else /* disable RX to be sure */
- outb(inb(ICEREG1724(ice, IRQMASK)) |
- VT1724_IRQ_MPU_RX,
- ICEREG1724(ice, IRQMASK));
+ spin_lock(&ice->reg_lock);
+ if (ice->midi_input)
+ vt1724_midi_read(ice);
+ else
+ vt1724_midi_clear_rx(ice);
+ spin_unlock(&ice->reg_lock);
}
/* ack MPU irq */
outb(status, ICEREG1724(ice, IRQSTAT));
@@ -349,8 +468,8 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
/* ought to really handle this properly */
if (mtstat & VT1724_MULTI_FIFO_ERR) {
unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR));
- outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));
- outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));
+ outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));
+ outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));
/* If I don't do this, I get machine lockup due to continual interrupts */
}
@@ -614,17 +733,17 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
outl(substream->runtime->dma_addr, ICEMT1724(ice, PLAYBACK_ADDR));
size = (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1;
- // outl(size, ICEMT1724(ice, PLAYBACK_SIZE));
+ /* outl(size, ICEMT1724(ice, PLAYBACK_SIZE)); */
outw(size, ICEMT1724(ice, PLAYBACK_SIZE));
outb(size >> 16, ICEMT1724(ice, PLAYBACK_SIZE) + 2);
size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
- // outl(size, ICEMT1724(ice, PLAYBACK_COUNT));
+ /* outl(size, ICEMT1724(ice, PLAYBACK_COUNT)); */
outw(size, ICEMT1724(ice, PLAYBACK_COUNT));
outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);
spin_unlock_irq(&ice->reg_lock);
- // printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream));
+ /* printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); */
return 0;
}
@@ -652,7 +771,7 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea
ptr = inl(ICEMT1724(ice, PLAYBACK_SIZE)) & 0xffffff;
ptr = (ptr + 1) << 2;
ptr = bytes_to_frames(substream->runtime, ptr);
- if (! ptr)
+ if (!ptr)
;
else if (ptr <= substream->runtime->buffer_size)
ptr = substream->runtime->buffer_size - ptr;
@@ -696,7 +815,7 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
ptr = inw(ice->profi_port + reg->size);
ptr = (ptr + 1) << 2;
ptr = bytes_to_frames(substream->runtime, ptr);
- if (! ptr)
+ if (!ptr)
;
else if (ptr <= substream->runtime->buffer_size)
ptr = substream->runtime->buffer_size - ptr;
@@ -723,8 +842,7 @@ static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
.start = VT1724_RDMA0_START,
};
-static const struct snd_pcm_hardware snd_vt1724_playback_pro =
-{
+static const struct snd_pcm_hardware snd_vt1724_playback_pro = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -742,8 +860,7 @@ static const struct snd_pcm_hardware snd_vt1724_playback_pro =
.periods_max = 1024,
};
-static const struct snd_pcm_hardware snd_vt1724_spdif =
-{
+static const struct snd_pcm_hardware snd_vt1724_spdif = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -764,8 +881,7 @@ static const struct snd_pcm_hardware snd_vt1724_spdif =
.periods_max = 1024,
};
-static const struct snd_pcm_hardware snd_vt1724_2ch_stereo =
-{
+static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -823,7 +939,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
- int chs;
+ int chs, num_indeps;
runtime->private_data = (void *)&vt1724_playback_pro_reg;
ice->playback_pro_substream = substream;
@@ -833,7 +949,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
set_rate_constraints(ice, substream);
mutex_lock(&ice->open_mutex);
/* calculate the currently available channels */
- for (chs = 0; chs < 3; chs++) {
+ num_indeps = ice->num_total_dacs / 2 - 1;
+ for (chs = 0; chs < num_indeps; chs++) {
if (ice->pcm_reserved[chs])
break;
}
@@ -910,7 +1027,7 @@ static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
.pointer = snd_vt1724_pcm_pointer,
};
-static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 * ice, int device)
+static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
@@ -995,7 +1112,7 @@ static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)
static int snd_vt1724_playback_spdif_prepare(struct snd_pcm_substream *substream)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
- if (! ice->force_pdma4)
+ if (!ice->force_pdma4)
update_spdif_rate(ice, substream->runtime->rate);
return snd_vt1724_pcm_prepare(substream);
}
@@ -1095,7 +1212,7 @@ static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
};
-static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device)
+static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
{
char *name;
struct snd_pcm *pcm;
@@ -1114,7 +1231,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device)
ice->has_spdif = 1;
} else
capt = 0;
- if (! play && ! capt)
+ if (!play && !capt)
return 0; /* no spdif device */
if (ice->force_pdma4 || ice->force_rdma1)
@@ -1229,7 +1346,7 @@ static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
};
-static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 * ice, int device)
+static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int play;
@@ -1264,11 +1381,11 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 * ice, int device)
* Mixer section
*/
-static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 * ice)
+static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
{
int err;
- if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
+ if (!(ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
static struct snd_ac97_bus_ops ops = {
@@ -1281,11 +1398,13 @@ static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 * ice)
mdelay(5); /* FIXME */
outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
- if ((err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = ice;
- if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
+ if (err < 0)
printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
else
return 0;
@@ -1306,7 +1425,7 @@ static inline unsigned int eeprom_triple(struct snd_ice1712 *ice, int idx)
((unsigned int)ice->eeprom.data[idx + 2] << 16);
}
-static void snd_vt1724_proc_read(struct snd_info_entry *entry,
+static void snd_vt1724_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ice1712 *ice = entry->private_data;
@@ -1348,11 +1467,11 @@ static void snd_vt1724_proc_read(struct snd_info_entry *entry,
idx, inb(ice->profi_port+idx));
}
-static void __devinit snd_vt1724_proc_init(struct snd_ice1712 * ice)
+static void __devinit snd_vt1724_proc_init(struct snd_ice1712 *ice)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "ice1724", &entry))
+ if (!snd_card_proc_new(ice->card, "ice1724", &entry))
snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);
}
@@ -1372,7 +1491,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+
memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
return 0;
}
@@ -1487,13 +1606,13 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
if (val != old)
update_spdif_bits(ice, val);
spin_unlock_irq(&ice->reg_lock);
- return (val != old);
+ return val != old;
}
static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.info = snd_vt1724_spdif_info,
.get = snd_vt1724_spdif_default_get,
.put = snd_vt1724_spdif_default_put
@@ -1526,7 +1645,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
.info = snd_vt1724_spdif_info,
.get = snd_vt1724_spdif_maskc_get,
};
@@ -1535,7 +1654,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
.info = snd_vt1724_spdif_info,
.get = snd_vt1724_spdif_maskp_get,
};
@@ -1572,8 +1691,8 @@ static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* FIXME: the following conflict with IEC958 Playback Route */
- // .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
- .name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
+ /* .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), */
+ .name = SNDRV_CTL_NAME_IEC958("Output ", NONE, SWITCH),
.info = snd_vt1724_spdif_sw_info,
.get = snd_vt1724_spdif_sw_get,
.put = snd_vt1724_spdif_sw_put
@@ -1593,7 +1712,7 @@ int snd_vt1724_gpio_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
-
+
snd_ice1712_save_gpio_status(ice);
ucontrol->value.integer.value[0] =
(snd_ice1712_gpio_read(ice) & (1 << shift) ? 1 : 0) ^ invert;
@@ -1648,7 +1767,7 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int i, rate;
-
+
spin_lock_irq(&ice->reg_lock);
if (ice->is_spdif_master(ice)) {
ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
@@ -1804,7 +1923,7 @@ static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol,
"H/W In 0", "H/W In 1", /* 1-2 */
"IEC958 In L", "IEC958 In R", /* 3-4 */
};
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 5;
@@ -1834,7 +1953,7 @@ static int get_route_val(struct snd_ice1712 *ice, int shift)
val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
val >>= shift;
- val &= 7; //we now have 3 bits per output
+ val &= 7; /* we now have 3 bits per output */
eitem = xlate[val];
if (eitem == 255) {
snd_BUG();
@@ -1913,7 +2032,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_vt1724_pro_route_info,
.get = snd_vt1724_pro_route_spdif_get,
.put = snd_vt1724_pro_route_spdif_put,
@@ -1936,7 +2055,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
-
+
spin_lock_irq(&ice->reg_lock);
for (idx = 0; idx < 22; idx++) {
outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX));
@@ -1963,7 +2082,7 @@ static struct snd_ice1712_card_info no_matched __devinitdata;
static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_revo_cards,
- snd_vt1724_amp_cards,
+ snd_vt1724_amp_cards,
snd_vt1724_aureon_cards,
snd_vt1720_mobo_cards,
snd_vt1720_pontis_cards,
@@ -2001,7 +2120,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
wait_i2c_busy(ice);
val = inb(ICEREG1724(ice, I2C_DATA));
mutex_unlock(&ice->i2c_mutex);
- //printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
+ /* printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val); */
return val;
}
@@ -2010,7 +2129,7 @@ void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
{
mutex_lock(&ice->i2c_mutex);
wait_i2c_busy(ice);
- //printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
+ /* printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data); */
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
outb(data, ICEREG1724(ice, I2C_DATA));
outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
@@ -2025,13 +2144,13 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
unsigned int i, size;
struct snd_ice1712_card_info * const *tbl, *c;
- if (! modelname || ! *modelname) {
+ if (!modelname || !*modelname) {
ice->eeprom.subvendor = 0;
if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) != 0)
ice->eeprom.subvendor =
(snd_vt1724_read_i2c(ice, dev, 0x00) << 0) |
- (snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |
- (snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |
+ (snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |
+ (snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |
(snd_vt1724_read_i2c(ice, dev, 0x03) << 24);
if (ice->eeprom.subvendor == 0 ||
ice->eeprom.subvendor == (unsigned int)-1) {
@@ -2054,13 +2173,13 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
if (modelname && c->model &&
- ! strcmp(modelname, c->model)) {
+ !strcmp(modelname, c->model)) {
printk(KERN_INFO "ice1724: Using board model %s\n",
c->name);
ice->eeprom.subvendor = c->subvendor;
} else if (c->subvendor != ice->eeprom.subvendor)
continue;
- if (! c->eeprom_size || ! c->eeprom_data)
+ if (!c->eeprom_size || !c->eeprom_data)
goto found;
/* if the EEPROM is given by the driver, use it */
snd_printdd("using the defined eeprom..\n");
@@ -2131,7 +2250,8 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
int err;
struct snd_kcontrol *kctl;
- snd_assert(ice->pcm != NULL, return -EIO);
+ if (snd_BUG_ON(!ice->pcm))
+ return -EIO;
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
if (err < 0)
@@ -2201,13 +2321,13 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
static int snd_vt1724_free(struct snd_ice1712 *ice)
{
- if (! ice->port)
+ if (!ice->port)
goto __hw_end;
/* mask all interrupts */
outb(0xff, ICEMT1724(ice, DMA_INT_MASK));
outb(0xff, ICEREG1724(ice, IRQMASK));
/* --- */
- __hw_end:
+__hw_end:
if (ice->irq >= 0)
free_irq(ice->irq, ice);
pci_release_regions(ice->pci);
@@ -2227,7 +2347,7 @@ static int snd_vt1724_dev_free(struct snd_device *device)
static int __devinit snd_vt1724_create(struct snd_card *card,
struct pci_dev *pci,
const char *modelname,
- struct snd_ice1712 ** r_ice1712)
+ struct snd_ice1712 **r_ice1712)
{
struct snd_ice1712 *ice;
int err;
@@ -2238,8 +2358,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
*r_ice1712 = NULL;
- /* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ /* enable PCI device */
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
ice = kzalloc(sizeof(*ice), GFP_KERNEL);
@@ -2263,7 +2384,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
snd_vt1724_proc_init(ice);
synchronize_irq(pci->irq);
- if ((err = pci_request_regions(pci, "ICE1724")) < 0) {
+ err = pci_request_regions(pci, "ICE1724");
+ if (err < 0) {
kfree(ice);
pci_disable_device(pci);
return err;
@@ -2298,9 +2420,10 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
*/
outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
+ if (err < 0) {
snd_vt1724_free(ice);
- return err;
+ return err;
}
snd_card_set_dev(card, &pci->dev);
@@ -2338,8 +2461,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
strcpy(card->driver, "ICE1724");
strcpy(card->shortname, "ICEnsemble ICE1724");
-
- if ((err = snd_vt1724_create(card, pci, model[dev], &ice)) < 0) {
+
+ err = snd_vt1724_create(card, pci, model[dev], &ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -2351,7 +2475,8 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
if (c->driver) /* specific driver? */
strcpy(card->driver, c->driver);
if (c->chip_init) {
- if ((err = c->chip_init(ice)) < 0) {
+ err = c->chip_init(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -2361,15 +2486,15 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
}
}
c = &no_matched;
- __found:
- /*
- * VT1724 has separate DMAs for the analog and the SPDIF streams while
- * ICE1712 has only one for both (mixed up).
- *
- * Confusingly the analog PCM is named "professional" here because it
- * was called so in ice1712 driver, and vt1724 driver is derived from
- * ice1712 driver.
- */
+__found:
+ /*
+ * VT1724 has separate DMAs for the analog and the SPDIF streams while
+ * ICE1712 has only one for both (mixed up).
+ *
+ * Confusingly the analog PCM is named "professional" here because it
+ * was called so in ice1712 driver, and vt1724 driver is derived from
+ * ice1712 driver.
+ */
ice->pro_rate_default = PRO_RATE_DEFAULT;
if (!ice->is_spdif_master)
ice->is_spdif_master = stdclock_is_spdif_master;
@@ -2384,76 +2509,86 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
if (!ice->hw_rates)
set_std_hw_rates(ice);
- if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) {
+ err = snd_vt1724_pcm_profi(ice, pcm_dev++);
+ if (err < 0) {
snd_card_free(card);
return err;
}
-
- if ((err = snd_vt1724_pcm_spdif(ice, pcm_dev++)) < 0) {
+
+ err = snd_vt1724_pcm_spdif(ice, pcm_dev++);
+ if (err < 0) {
snd_card_free(card);
return err;
}
-
- if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) {
+
+ err = snd_vt1724_pcm_indep(ice, pcm_dev++);
+ if (err < 0) {
snd_card_free(card);
return err;
}
- if ((err = snd_vt1724_ac97_mixer(ice)) < 0) {
+ err = snd_vt1724_ac97_mixer(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
- if ((err = snd_vt1724_build_controls(ice)) < 0) {
+ err = snd_vt1724_build_controls(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
- if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) {
+ err = snd_vt1724_spdif_build_controls(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
}
if (c->build_controls) {
- if ((err = c->build_controls(ice)) < 0) {
+ err = c->build_controls(ice);
+ if (err < 0) {
snd_card_free(card);
return err;
}
}
- if (! c->no_mpu401) {
+ if (!c->no_mpu401) {
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
- struct snd_mpu401 *mpu;
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
- ICEREG1724(ice, MPU_CTRL),
- (MPU401_INFO_INTEGRATED |
- MPU401_INFO_NO_ACK |
- MPU401_INFO_TX_IRQ),
- ice->irq, 0,
- &ice->rmidi[0])) < 0) {
+ struct snd_rawmidi *rmidi;
+
+ err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
+ if (err < 0) {
snd_card_free(card);
return err;
}
- mpu = ice->rmidi[0]->private_data;
- mpu->read = snd_vt1724_mpu401_read;
- mpu->write = snd_vt1724_mpu401_write;
- /* unmask MPU RX/TX irqs */
- outb(inb(ICEREG1724(ice, IRQMASK)) &
- ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
- ICEREG1724(ice, IRQMASK));
+ ice->rmidi[0] = rmidi;
+ rmidi->private_data = ice;
+ strcpy(rmidi->name, "ICE1724 MIDI");
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &vt1724_midi_output_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &vt1724_midi_input_ops);
+
/* set watermarks */
outb(VT1724_MPU_RX_FIFO | 0x1,
ICEREG1724(ice, MPU_FIFO_WM));
outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
+ /* set UART mode */
+ outb(VT1724_MPU_UART, ICEREG1724(ice, MPU_CTRL));
}
}
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, ice->port, ice->irq);
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_card_free(card);
return err;
}
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index b4e0c16852a..c51659b9caf 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -21,7 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
#include <asm/io.h>
#include <linux/delay.h>
@@ -34,9 +34,10 @@
#include "ice1712.h"
#include "envy24ht.h"
#include "juli.h"
+
struct juli_spec {
struct ak4114 *ak4114;
- unsigned int analog: 1;
+ unsigned int analog:1;
};
/*
@@ -160,14 +161,17 @@ static int get_gpio_val(int rate)
return 0;
}
-static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val)
+static void juli_ak4114_write(void *private_data, unsigned char reg,
+ unsigned char val)
{
- snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg, val);
+ snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR,
+ reg, val);
}
-
+
static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
{
- return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg);
+ return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data,
+ AK4114_ADDR, reg);
}
/*
@@ -175,7 +179,7 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
* to the external rate
*/
static void juli_spdif_in_open(struct snd_ice1712 *ice,
- struct snd_pcm_substream *substream)
+ struct snd_pcm_substream *substream)
{
struct juli_spec *spec = ice->spec;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -208,7 +212,8 @@ static void juli_akm_write(struct snd_akm4xxx *ak, int chip,
{
struct snd_ice1712 *ice = ak->private_data[0];
- snd_assert(chip == 0, return);
+ if (snd_BUG_ON(chip))
+ return;
snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data);
}
@@ -571,10 +576,12 @@ static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,
static int __devinit juli_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
- /* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
+ /* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN |
+ AK4114_OCKS0 | AK4114_OCKS1,
/* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S,
/* AK4114_REG_IO0 */ AK4114_TX1E,
- /* AK4114_REG_IO1 */ AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
+ /* AK4114_REG_IO1 */ AK4114_EFH_1024 | AK4114_DIT |
+ AK4114_IPS(1),
/* AK4114_REG_INT0_MASK */ 0,
/* AK4114_REG_INT1_MASK */ 0
};
@@ -604,12 +611,14 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
spec->ak4114->check_flags = 0;
#if 0
- /* 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 */
+/*
+ * it seems that the analog doughter board detection does not work reliably, so
+ * force the analog flag; it should be very rare (if ever) to come at Juli@
+ * used without the analog daughter board
+ */
spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
#else
- spec->analog = 1;
+ spec->analog = 1;
#endif
if (spec->analog) {
@@ -617,14 +626,16 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
- ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
- if (! ak)
+ ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ ak = ice->akm;
+ if (!ak)
return -ENOMEM;
ice->akm_codecs = 1;
- if ((err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice)) < 0)
+ err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice);
+ if (err < 0)
return err;
}
-
+
/* juli is clocked by Xilinx array */
ice->hw_rates = &juli_rates_info;
ice->is_spdif_master = juli_is_spdif_master;
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index 5a158b73dca..de29be8c965 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -22,15 +22,24 @@
*/
/* PHASE 22 overview:
- * Audio controller: VIA Envy24HT-S (slightly trimmed down version of Envy24HT)
+ * Audio controller: VIA Envy24HT-S (slightly trimmed down Envy24HT, 4in/4out)
* Analog chip: AK4524 (partially via Philip's 74HCT125)
- * Digital receiver: CS8414-CS (not supported in this release)
+ * Digital receiver: CS8414-CS (supported in this release)
+ * PHASE 22 revision 2.0 and Terrasoniq/Musonik TS22PCI have CS8416
+ * (support status unknown, please test and report)
*
* Envy connects to AK4524
* - CS directly from GPIO 10
* - CCLK via 74HCT125's gate #4 from GPIO 4
* - CDTI via 74HCT125's gate #2 from GPIO 5
- * CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
+ * CDTI may be completely blocked by 74HCT125's gate #1
+ * controlled by GPIO 3
+ */
+
+/* PHASE 28 overview:
+ * Audio controller: VIA Envy24HT (full untrimmed version, 4in/8out)
+ * Analog chip: WM8770 (8 channel 192k DAC, 2 channel 96k ADC)
+ * Digital receiver: CS8414-CS (supported in this release)
*/
#include <asm/io.h>
@@ -77,18 +86,18 @@ struct phase28_spec {
* 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
+ 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)
@@ -117,26 +126,31 @@ static int __devinit phase22_init(struct snd_ice1712 *ice)
struct snd_akm4xxx *ak;
int err;
- // Configure DAC/ADC description for generic part of ice1724
+ /* Configure DAC/ADC description for generic part of ice1724 */
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_PHASE22:
+ case VT1724_SUBDEVICE_TS22:
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
- ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
+ ice->vt1720 = 1; /* Envy24HT-S have 16 bit wide GPIO */
break;
default:
snd_BUG();
return -EINVAL;
}
- // Initialize analog chips
- ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
- if (! ak)
+ /* Initialize analog chips */
+ ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ ak = ice->akm;
+ if (!ak)
return -ENOMEM;
ice->akm_codecs = 1;
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_PHASE22:
- if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
+ case VT1724_SUBDEVICE_TS22:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_phase22,
+ &akm_phase22_priv, ice);
+ if (err < 0)
return err;
break;
}
@@ -150,6 +164,7 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_PHASE22:
+ case VT1724_SUBDEVICE_TS22:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
@@ -158,9 +173,10 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
}
static unsigned char phase22_eeprom[] __devinitdata = {
- [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */
+ [ICE_EEP2_SYSCONF] = 0x28, /* clock 512, mpu 401,
+ spdif-in/1xADC, 1xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
- [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */
+ [ICE_EEP2_I2S] = 0xf0, /* vol, 96k, 24bit */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0xff,
[ICE_EEP2_GPIO_DIR1] = 0xff,
@@ -174,7 +190,8 @@ static unsigned char phase22_eeprom[] __devinitdata = {
};
static unsigned char phase28_eeprom[] __devinitdata = {
- [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401,
+ spdif-in/1xADC, 4xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
@@ -192,15 +209,16 @@ static unsigned char phase28_eeprom[] __devinitdata = {
/*
* write data in the SPI mode
*/
-static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits)
+static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs,
+ unsigned int data, int bits)
{
unsigned int tmp;
int i;
tmp = snd_ice1712_gpio_read(ice);
- snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
- PHASE28_WM_CS));
+ snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|
+ PHASE28_SPI_CLK|PHASE28_WM_CS));
tmp |= PHASE28_WM_RW;
tmp &= ~cs;
snd_ice1712_gpio_write(ice, tmp);
@@ -259,14 +277,16 @@ static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
ice->akm[0].images[reg + 1] = val;
}
-static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
+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 = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
+ nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) *
+ (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
wm_put(ice, index, nvol);
wm_put_nocache(ice, index, 0x180 | nvol);
@@ -277,17 +297,20 @@ static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned sho
*/
#define wm_pcm_mute_info snd_ctl_boolean_mono_info
-static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_mute_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_MUTE) & 0x10) ? 0 : 1;
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ?
+ 0 : 1;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
-static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short nval, oval;
@@ -296,7 +319,8 @@ static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
snd_ice1712_save_gpio_status(ice);
oval = wm_get(ice, WM_MUTE);
nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
- if ((change = (nval != oval)))
+ change = (nval != oval);
+ if (change)
wm_put(ice, WM_MUTE, nval);
snd_ice1712_restore_gpio_status(ice);
@@ -306,7 +330,8 @@ static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
/*
* Master volume attenuation mixer control
*/
-static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+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;
@@ -315,17 +340,20 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return 0;
}
-static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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] = spec->master[i] & ~WM_VOL_MUTE;
+ for (i = 0; i < 2; i++)
+ 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)
+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;
@@ -355,38 +383,38 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits_phase28[] = {
/* These come first to reduce init pop noise */
- 0x1b, 0x044, /* ADC Mux (AC'97 source) */
- 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
- 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
-
- 0x18, 0x000, /* All power-up */
-
- 0x16, 0x122, /* I2S, normal polarity, 24bit */
- 0x17, 0x022, /* 256fs, slave mode */
- 0x00, 0, /* DAC1 analog mute */
- 0x01, 0, /* DAC2 analog mute */
- 0x02, 0, /* DAC3 analog mute */
- 0x03, 0, /* DAC4 analog mute */
- 0x04, 0, /* DAC5 analog mute */
- 0x05, 0, /* DAC6 analog mute */
- 0x06, 0, /* DAC7 analog mute */
- 0x07, 0, /* DAC8 analog mute */
- 0x08, 0x100, /* master analog mute */
- 0x09, 0xff, /* DAC1 digital full */
- 0x0a, 0xff, /* DAC2 digital full */
- 0x0b, 0xff, /* DAC3 digital full */
- 0x0c, 0xff, /* DAC4 digital full */
- 0x0d, 0xff, /* DAC5 digital full */
- 0x0e, 0xff, /* DAC6 digital full */
- 0x0f, 0xff, /* DAC7 digital full */
- 0x10, 0xff, /* DAC8 digital full */
- 0x11, 0x1ff, /* master digital full */
- 0x12, 0x000, /* phase normal */
- 0x13, 0x090, /* unmute DAC L/R */
- 0x14, 0x000, /* all unmute */
- 0x15, 0x000, /* no deemphasis, no ZFLG */
- 0x19, 0x000, /* -12dB ADC/L */
- 0x1a, 0x000, /* -12dB ADC/R */
+ 0x1b, 0x044, /* ADC Mux (AC'97 source) */
+ 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
+ 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
+
+ 0x18, 0x000, /* All power-up */
+
+ 0x16, 0x122, /* I2S, normal polarity, 24bit */
+ 0x17, 0x022, /* 256fs, slave mode */
+ 0x00, 0, /* DAC1 analog mute */
+ 0x01, 0, /* DAC2 analog mute */
+ 0x02, 0, /* DAC3 analog mute */
+ 0x03, 0, /* DAC4 analog mute */
+ 0x04, 0, /* DAC5 analog mute */
+ 0x05, 0, /* DAC6 analog mute */
+ 0x06, 0, /* DAC7 analog mute */
+ 0x07, 0, /* DAC8 analog mute */
+ 0x08, 0x100, /* master analog mute */
+ 0x09, 0xff, /* DAC1 digital full */
+ 0x0a, 0xff, /* DAC2 digital full */
+ 0x0b, 0xff, /* DAC3 digital full */
+ 0x0c, 0xff, /* DAC4 digital full */
+ 0x0d, 0xff, /* DAC5 digital full */
+ 0x0e, 0xff, /* DAC6 digital full */
+ 0x0f, 0xff, /* DAC7 digital full */
+ 0x10, 0xff, /* DAC8 digital full */
+ 0x11, 0x1ff, /* master digital full */
+ 0x12, 0x000, /* phase normal */
+ 0x13, 0x090, /* unmute DAC L/R */
+ 0x14, 0x000, /* all unmute */
+ 0x15, 0x000, /* no deemphasis, no ZFLG */
+ 0x19, 0x000, /* -12dB ADC/L */
+ 0x1a, 0x000, /* -12dB ADC/R */
(unsigned short)-1
};
@@ -404,17 +432,19 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
return -ENOMEM;
ice->spec = spec;
- // Initialize analog chips
- ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ /* Initialize analog chips */
+ ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ ak = ice->akm;
if (!ak)
return -ENOMEM;
ice->akm_codecs = 1;
- snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
+ snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for time being */
/* reset the wm codec as the SPI mode */
snd_ice1712_save_gpio_status(ice);
- snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
+ snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|
+ PHASE28_HP_SEL));
tmp = snd_ice1712_gpio_read(ice);
tmp &= ~PHASE28_WM_RESET;
@@ -446,7 +476,8 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
/*
* DAC volume attenuation mixer control
*/
-static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int wm_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;
@@ -456,7 +487,8 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
return 0;
}
-static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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;
@@ -470,7 +502,8 @@ static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
return 0;
}
-static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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;
@@ -501,7 +534,8 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
/*
* WM8770 mute control
*/
-static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int wm_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = kcontrol->private_value >> 8;
uinfo->value.integer.min = 0;
@@ -509,7 +543,8 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
return 0;
}
-static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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;
@@ -524,7 +559,8 @@ static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
return 0;
}
-static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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;
@@ -539,9 +575,10 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
if (ucontrol->value.integer.value[i] != val) {
spec->vol[ofs + i] &= ~WM_VOL_MUTE;
spec->vol[ofs + i] |=
- ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+ ucontrol->value.integer.value[i] ? 0 :
+ WM_VOL_MUTE;
wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
- spec->master[i]);
+ spec->master[i]);
change = 1;
}
}
@@ -555,7 +592,8 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
*/
#define wm_master_mute_info snd_ctl_boolean_stereo_info
-static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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;
@@ -567,7 +605,8 @@ static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return 0;
}
-static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+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;
@@ -580,11 +619,12 @@ static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int dac;
spec->master[i] &= ~WM_VOL_MUTE;
spec->master[i] |=
- ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+ 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,
- spec->vol[dac + i],
- spec->master[i]);
+ spec->vol[dac + i],
+ spec->master[i]);
change = 1;
}
}
@@ -597,7 +637,8 @@ static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
#define PCM_0dB 0xff
#define PCM_RES 128 /* -64dB */
#define PCM_MIN (PCM_0dB - PCM_RES)
-static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
@@ -606,7 +647,8 @@ static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in
return 0;
}
-static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
@@ -619,7 +661,8 @@ static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
return 0;
}
-static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short ovol, nvol;
@@ -633,7 +676,8 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
if (ovol != nvol) {
wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
- wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
+ /* update */
+ wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100);
change = 1;
}
snd_ice1712_restore_gpio_status(ice);
@@ -645,18 +689,22 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
*/
#define phase28_deemp_info snd_ctl_boolean_mono_info
-static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_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;
+ ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) ==
+ 0xf;
return 0;
}
-static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_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);
+ temp = wm_get(ice, WM_DAC_CTRL2);
+ temp2 = temp;
if (ucontrol->value.integer.value[0])
temp |= 0xf;
else
@@ -671,7 +719,8 @@ static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
/*
* ADC Oversampling
*/
-static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
+static int phase28_oversampling_info(struct snd_kcontrol *k,
+ struct snd_ctl_elem_info *uinfo)
{
static char *texts[2] = { "128x", "64x" };
@@ -680,25 +729,31 @@ static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem
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]);
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items -
+ 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
- return 0;
+ return 0;
}
-static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_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;
+ ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) ==
+ 0x8;
return 0;
}
-static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_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);
+ temp = wm_get(ice, WM_MASTER);
+ temp2 = temp;
if (ucontrol->value.enumerated.item[0])
temp |= 0x8;
@@ -871,13 +926,16 @@ static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
counts = ARRAY_SIZE(phase28_dac_controls);
for (i = 0; i < counts; i++) {
- err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&phase28_dac_controls[i],
+ ice));
if (err < 0)
return err;
}
for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
- err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&wm_controls[i], ice));
if (err < 0)
return err;
}
@@ -904,5 +962,14 @@ struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
.eeprom_size = sizeof(phase28_eeprom),
.eeprom_data = phase28_eeprom,
},
+ {
+ .subvendor = VT1724_SUBDEVICE_TS22,
+ .name = "Terrasoniq TS22 PCI",
+ .model = "TS22",
+ .chip_init = phase22_init,
+ .build_controls = phase22_add_controls,
+ .eeprom_size = sizeof(phase22_eeprom),
+ .eeprom_data = phase22_eeprom,
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
index 13e841b5548..7fc22d9d442 100644
--- a/sound/pci/ice1712/phase.h
+++ b/sound/pci/ice1712/phase.h
@@ -22,13 +22,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
-#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
- "{Terratec,Phase 28},"
+#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
+ "{Terratec,Phase 28},"\
+ "{Terrasoniq,TS22},"
#define VT1724_SUBDEVICE_PHASE22 0x3b155011
#define VT1724_SUBDEVICE_PHASE28 0x3b154911
+#define VT1724_SUBDEVICE_TS22 0x3b157b11
/* entry point */
extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 203cdc1bf8d..6bc3f91b728 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -43,7 +43,8 @@
/* 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_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
@@ -740,7 +741,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */
WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */
WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */
- // WM_DAC_MASTER, 0x0100, /* DAC master muted */
+ /* WM_DAC_MASTER, 0x0100, */ /* DAC master muted */
WM_PHASE_SWAP, 0x0000, /* phase normal */
WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */
WM_ADC_ATTEN_L, 0x0000, /* ADC muted */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index 4d2631434dc..b508bb360b9 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -1,7 +1,7 @@
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
- * Lowlevel functions for M-Audio Revolution 7.1
+ * Lowlevel functions for M-Audio Audiophile 192, Revolution 7.1 and 5.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
*
@@ -48,7 +48,7 @@ static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
}
/*
- * change the rate of envy24HT, AK4355 and AK4381
+ * change the rate of Envy24HT, AK4355 and AK4381
*/
static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
@@ -83,8 +83,8 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
tmp = snd_akm4xxx_get(ak, 0, reg);
tmp &= ~(0x03 << shift);
tmp |= dfs << shift;
- // snd_akm4xxx_write(ak, 0, reg, tmp);
- snd_akm4xxx_set(ak, 0, reg, tmp); /* the value is written in reset(0) */
+ /* snd_akm4xxx_write(ak, 0, reg, tmp); */
+ snd_akm4xxx_set(ak, 0, reg, tmp); /* value is written in reset(0) */
snd_akm4xxx_reset(ak, 0);
}
@@ -216,6 +216,7 @@ static const struct snd_akm4xxx_dac_channel revo51_dac[] = {
AK_DAC("PCM Center Playback Volume", 1),
AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Rear Playback Volume", 2),
+ AK_DAC("PCM Headphone Volume", 2),
};
static const char *revo51_adc_input_names[] = {
@@ -279,7 +280,7 @@ static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
static struct snd_akm4xxx akm_revo51 __devinitdata = {
.type = SND_AK4358,
- .num_dacs = 6,
+ .num_dacs = 8,
.ops = {
.set_rate_val = revo_set_rate_val
},
@@ -508,7 +509,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
ice->gpio.i2s_mclk_changed = revo_i2s_mclk_changed;
break;
case VT1724_SUBDEVICE_REVOLUTION51:
- ice->num_total_dacs = 6;
+ ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
break;
case VT1724_SUBDEVICE_AUDIOPHILE192:
@@ -524,16 +525,20 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
ak = ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ak)
return -ENOMEM;
- ice->akm_codecs = 2;
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_REVOLUTION71:
ice->akm_codecs = 2;
- if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front, &akm_revo_front_priv, ice)) < 0)
+ err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front,
+ &akm_revo_front_priv, ice);
+ if (err < 0)
return err;
- if ((err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, &akm_revo_surround_priv, ice)) < 0)
+ err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo_surround,
+ &akm_revo_surround_priv, ice);
+ if (err < 0)
return err;
/* unmute all codecs */
- snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
+ snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
+ VT1724_REVO_MUTE);
break;
case VT1724_SUBDEVICE_REVOLUTION51:
ice->akm_codecs = 2;
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index a08d17c7e65..5af9e84456d 100644
--- a/sound/pci/ice1712/wtm.c
+++ b/sound/pci/ice1712/wtm.c
@@ -1,12 +1,12 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
- *
+ *
* Lowlevel functions for Ego Sys Waveterminal 192M
*
* Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
* Some functions are taken from the Prodigy192 driver
* source
- *
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -20,12 +20,12 @@
* 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/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -39,9 +39,9 @@
/*
- * 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
+ * 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
*/
-static inline void stac9460_put(struct snd_ice1712 *ice, int reg,
+static inline void stac9460_put(struct snd_ice1712 *ice, int reg,
unsigned char val)
{
snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val);
@@ -73,7 +73,7 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
#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)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
@@ -88,14 +88,14 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
}
if (id < 6)
val = stac9460_get(ice, idx);
- else
- val = stac9460_2_get(ice,idx - 6);
+ else
+ val = stac9460_2_get(ice, idx - 6);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
return 0;
}
static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
@@ -105,8 +105,8 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
old = stac9460_get(ice, idx);
- new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) |
- (old & ~0x80);
+ new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
+ (old & ~0x80);
change = (new != old);
if (change) {
stac9460_put(ice, idx, new);
@@ -117,16 +117,16 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
idx = id + STAC946X_LF_VOLUME;
if (id < 6)
old = stac9460_get(ice, idx);
- else
+ else
old = stac9460_2_get(ice, idx - 6);
- new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) |
+ new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
(old & ~0x80);
change = (new != old);
if (change) {
if (id < 6)
- stac9460_put(ice, idx, new);
+ stac9460_put(ice, idx, new);
else
- stac9460_2_put(ice, idx - 6, new);
+ stac9460_2_put(ice, idx - 6, new);
}
}
return change;
@@ -136,7 +136,7 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
* DAC volume attenuation mixer control
*/
static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
@@ -146,7 +146,7 @@ static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,
}
static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx, id;
@@ -161,14 +161,14 @@ static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,
}
if (id < 6)
vol = stac9460_get(ice, idx) & 0x7f;
- else
+ else
vol = stac9460_2_get(ice, idx - 6) & 0x7f;
ucontrol->value.integer.value[0] = 0x7f - vol;
return 0;
}
static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx, id;
@@ -182,8 +182,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
- stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
- stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+ stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+ stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
} else {
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
@@ -191,17 +191,17 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
nvol = ucontrol->value.integer.value[0] & 0x7f;
if (id < 6)
tmp = stac9460_get(ice, idx);
- else
+ else
tmp = stac9460_2_get(ice, idx - 6);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
if (id < 6)
stac9460_put(ice, idx, (0x7f - nvol) |
- (tmp & 0x80));
- else
+ (tmp & 0x80));
+ else
stac9460_2_put(ice, idx-6, (0x7f - nvol) |
- (tmp & 0x80));
+ (tmp & 0x80));
}
}
return change;
@@ -213,12 +213,12 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
#define stac9460_adc_mute_info snd_ctl_boolean_stereo_info
static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int i, id;
-
+
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
@@ -235,20 +235,20 @@ static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,
}
static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
int i, reg, id;
int change;
-
+
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
old = stac9460_get(ice, reg);
new = (~ucontrol->value.integer.value[i]<<7&0x80) |
- (old&~0x80);
+ (old&~0x80);
change = (new != old);
if (change)
stac9460_put(ice, reg, new);
@@ -258,7 +258,7 @@ static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
reg = STAC946X_MIC_L_VOLUME + i;
old = stac9460_2_get(ice, reg);
new = (~ucontrol->value.integer.value[i]<<7&0x80) |
- (old&~0x80);
+ (old&~0x80);
change = (new != old);
if (change)
stac9460_2_put(ice, reg, new);
@@ -271,7 +271,7 @@ static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
*ADC gain mixer control
*/
static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
@@ -281,12 +281,12 @@ static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,
}
static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int i, reg, id;
unsigned char vol;
-
+
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
@@ -305,13 +305,13 @@ static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,
}
static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int i, reg, id;
unsigned char ovol, nvol;
int change;
-
+
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
@@ -321,7 +321,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
change = ((ovol & 0x0f) != nvol);
if (change)
stac9460_put(ice, reg, (0x0f - nvol) |
- (ovol & ~0x0f));
+ (ovol & ~0x0f));
}
} else {
for (i = 0; i < 2; ++i) {
@@ -331,7 +331,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
change = ((ovol & 0x0f) != nvol);
if (change)
stac9460_2_put(ice, reg, (0x0f - nvol) |
- (ovol & ~0x0f));
+ (ovol & ~0x0f));
}
}
return change;
@@ -344,23 +344,23 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
#define stac9460_mic_sw_info snd_ctl_boolean_mono_info
static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int id;
-
+
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0)
- val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
+ val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
- val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
+ val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
return 0;
}
static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
@@ -368,16 +368,16 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0)
- old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
+ old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
- old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
- new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
+ old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
+ new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
if (id == 0)
- stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
+ stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
else
- stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
+ stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
}
return change;
}
@@ -443,7 +443,7 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
- }
+ }
};
@@ -470,7 +470,7 @@ static int __devinit wtm_init(struct snd_ice1712 *ice)
(unsigned short)-1
};
unsigned short *p;
-
+
/*WTM 192M*/
ice->num_total_dacs = 8;
ice->num_total_adcs = 4;
diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h
index 03a394e442f..423c1a204c0 100644
--- a/sound/pci/ice1712/wtm.h
+++ b/sound/pci/ice1712/wtm.h
@@ -10,8 +10,8 @@
*/
#define AK4114_ADDR 0x20 /*S/PDIF receiver*/
-#define STAC9460_I2C_ADDR 0x54 /* ADC*2 | DAC*6 */
-#define STAC9460_2_I2C_ADDR 0x56 /* ADC|DAC *2 */
+#define STAC9460_I2C_ADDR 0x54 /* ADC*2 | DAC*6 */
+#define STAC9460_2_I2C_ADDR 0x56 /* ADC|DAC *2 */
extern struct snd_ice1712_card_info snd_vt1724_wtm_cards[];
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 048d99e25ab..c88d1eace1c 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -59,6 +59,12 @@ MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
"{SiS,SI7012},"
"{NVidia,nForce Audio},"
"{NVidia,nForce2 Audio},"
+ "{NVidia,nForce3 Audio},"
+ "{NVidia,MCP04},"
+ "{NVidia,MCP501},"
+ "{NVidia,CK804},"
+ "{NVidia,CK8},"
+ "{NVidia,CK8S},"
"{AMD,AMD768},"
"{AMD,AMD8111},"
"{ALI,M5455}}");
@@ -77,7 +83,7 @@ MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
module_param(ac97_clock, int, 0444);
-MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = whitelist + auto-detect, 1 = force autodetect).");
module_param(ac97_quirk, charp, 0444);
MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
module_param(buggy_semaphore, bool, 0444);
@@ -1957,6 +1963,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x10cf,
+ .subdevice = 0x127d,
+ .name = "Fujitsu Lifebook P7010",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .subvendor = 0x10cf,
.subdevice = 0x127e,
.name = "Fujitsu Lifebook C1211D",
.type = AC97_TUNE_HP_ONLY
@@ -2132,8 +2144,8 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
snd_intel8x0_codec_read_test(chip, codecs);
chip->ac97_sdin[codecs] =
igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
- snd_assert(chip->ac97_sdin[codecs] < 3,
- chip->ac97_sdin[codecs] = 0);
+ if (snd_BUG_ON(chip->ac97_sdin[codecs] >= 3))
+ chip->ac97_sdin[codecs] = 0;
} else
chip->ac97_sdin[codecs] = i;
codecs++;
@@ -2686,6 +2698,28 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
}
+static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = {
+ SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+ SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
+ SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
+ SND_PCI_QUIRK(0x1043, 0x80f3, "AD1985", 48000),
+ { } /* terminator */
+};
+
+static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip)
+{
+ struct pci_dev *pci = chip->pci;
+ const struct snd_pci_quirk *wl;
+
+ wl = snd_pci_quirk_lookup(pci, intel8x0_clock_list);
+ if (!wl)
+ return 0;
+ printk(KERN_INFO "intel8x0: white list rate for %04x:%04x is %i\n",
+ pci->subsystem_vendor, pci->subsystem_device, wl->value);
+ chip->ac97_bus->clock = wl->value;
+ return 1;
+}
+
#ifdef CONFIG_PROC_FS
static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
@@ -3081,8 +3115,14 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
"%s with %s at irq %i", card->shortname,
snd_ac97_get_short_name(chip->ac97[0]), chip->irq);
- if (! ac97_clock)
- intel8x0_measure_ac97_clock(chip);
+ if (ac97_clock == 0 || ac97_clock == 1) {
+ if (ac97_clock == 0) {
+ if (intel8x0_in_clock_list(chip) == 0)
+ intel8x0_measure_ac97_clock(chip);
+ } else {
+ intel8x0_measure_ac97_clock(chip);
+ }
+ }
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index faf674e671a..93449e46456 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -306,7 +306,8 @@ static unsigned int get_ich_codec_bit(struct intel8x0m *chip, unsigned int codec
static unsigned int codec_bit[3] = {
ICH_PCR, ICH_SCR, ICH_TCR
};
- snd_assert(codec < 3, return ICH_PCR);
+ if (snd_BUG_ON(codec >= 3))
+ return ICH_PCR;
return codec_bit[codec];
}
diff --git a/sound/pci/korg1212/korg1212-firmware.h b/sound/pci/korg1212/korg1212-firmware.h
deleted file mode 100644
index f6f5b91806a..00000000000
--- a/sound/pci/korg1212/korg1212-firmware.h
+++ /dev/null
@@ -1,987 +0,0 @@
-static char dspCode [] = {
-0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff,
-0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
-0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff,
-0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff,
-0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
-0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff,
-0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff,
-0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
-0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
-0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
-0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
-0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
-0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
-0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
-0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff,
-0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
-0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,
-0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff,
-0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff,
-0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
-0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
-0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
-0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
-0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,
-0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
-0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
-0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
-0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
-0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
-0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
-0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff,
-0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
-0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff,
-0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff,
-0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff,
-0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,
-0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
-0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
-0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff,
-0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
-0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
-0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
-0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
-0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
-0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,
-0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff,
-0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff,
-0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff,
-0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,
-0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
-0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff,
-0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,
-0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
-0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
-0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
-0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
-0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
-0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
-0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
-0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
-0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,
-0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,
-0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,
-0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
-0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
-0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
-0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
-0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
-0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,
-0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff,
-0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
-0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
-0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff,
-0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
-0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,
-0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,
-0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
-0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
-0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
-0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
-0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
-0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
-0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
-0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
-0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,
-0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff,
-0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,
-0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,
-0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
-0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
-0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,
-0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,
-0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
-0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff,
-0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff,
-0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
-0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
-0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
-0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff,
-0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
-0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff,
-0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff,
-0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff,
-0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
-0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff,
-0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,
-0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
-0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
-0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
-0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff,
-0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
-0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
-0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
-0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
-0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
-0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff,
-0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
-0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,
-0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
-0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
-0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
-0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
-0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,
-0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
-0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
-0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,
-0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
-0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,
-0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,
-0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
-0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
-0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
-0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
-0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
-0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
-0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,
-0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
-0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
-0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
-0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff,
-0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
-0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
-0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,
-0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,
-0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,
-0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
-0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,
-0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,
-0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
-0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
-0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
-0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff,
-0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,
-0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
-0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,
-0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
-0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff,
-0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff,
-0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff,
-0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
-0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
-0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
-0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
-0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
-0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
-0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
-0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
-0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,
-0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,
-0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff,
-0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
-0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
-0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff,
-0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
-0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
-0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
-0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
-0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
-0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
-0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
-0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
-0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
-0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
-0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
-0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
-0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
-0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,
-0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
-0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff,
-0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,
-0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
-0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
-0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,
-0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff,
-0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff,
-0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,
-0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
-0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
-0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
-0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
-0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
-0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff,
-0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
-0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,
-0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
-0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
-0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
-0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,
-0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff,
-0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
-0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
-0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
-0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
-0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
-0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
-0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff,
-0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff,
-0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
-0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
-0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
-0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,
-0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
-0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff,
-0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
-0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
-0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,
-0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
-0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
-0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,
-0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff,
-0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
-0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
-0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff,
-0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,
-0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
-0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
-0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,
-0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
-0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
-0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
-0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
-0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
-0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,
-0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
-0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
-0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
-0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
-0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
-0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
-0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
-0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
-0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff,
-0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff,
-0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
-0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
-0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
-0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
-0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,
-0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,
-0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
-0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
-0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
-0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff,
-0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
-0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff,
-0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff,
-0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
-0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
-0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff,
-0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
-0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
-0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
-0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,
-0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff,
-0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff,
-0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff,
-0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
-0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff,
-0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff };
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index f4c85b52bde..5f8006b4275 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -260,14 +260,6 @@ enum MonitorModeSelector {
#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement
// from the card after sending a command.
-#ifdef CONFIG_SND_KORG1212_FIRMWARE_IN_KERNEL
-#include "korg1212-firmware.h"
-static const struct firmware static_dsp_code = {
- .data = (u8 *)dspCode,
- .size = sizeof dspCode
-};
-#endif
-
enum ClockSourceIndex {
K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz
K1212_CLKIDX_AdatAt48K, // selects source as ADAT at 48 kHz
@@ -412,9 +404,7 @@ struct snd_korg1212 {
MODULE_DESCRIPTION("korg1212");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{KORG,korg1212}}");
-#ifndef CONFIG_SND_KORG1212_FIRMWARE_IN_KERNEL
MODULE_FIRMWARE("korg/k1212.dsp");
-#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
@@ -1291,7 +1281,8 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_silence pos=%d offset=%d size=%d count=%d\n",
pos, offset, size, count);
- snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+ if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
+ return -EINVAL;
for (i=0; i < count; i++) {
#if K1212_DEBUG_LEVEL > 0
@@ -1316,7 +1307,8 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst,
K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n",
pos, offset, size);
- snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+ if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
+ return -EINVAL;
for (i=0; i < count; i++) {
#if K1212_DEBUG_LEVEL > 0
@@ -1346,7 +1338,8 @@ static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *sr
K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n",
pos, offset, size, count);
- snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+ if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
+ return -EINVAL;
for (i=0; i < count; i++) {
#if K1212_DEBUG_LEVEL > 0
@@ -2348,9 +2341,6 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy +
offsetof(struct KorgSharedBuffer, AdatTimeCode);
-#ifdef CONFIG_SND_KORG1212_FIRMWARE_IN_KERNEL
- dsp_code = &static_dsp_code;
-#else
err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev);
if (err < 0) {
release_firmware(dsp_code);
@@ -2358,15 +2348,12 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
snd_korg1212_free(korg1212);
return err;
}
-#endif
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
dsp_code->size, &korg1212->dma_dsp) < 0) {
snd_printk(KERN_ERR "korg1212: cannot allocate dsp code memory (%zd bytes)\n", dsp_code->size);
snd_korg1212_free(korg1212);
-#ifndef CONFIG_SND_KORG1212_FIRMWARE_IN_KERNEL
release_firmware(dsp_code);
-#endif
return -ENOMEM;
}
@@ -2376,9 +2363,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
memcpy(korg1212->dma_dsp.area, dsp_code->data, dsp_code->size);
-#ifndef CONFIG_SND_KORG1212_FIRMWARE_IN_KERNEL
release_firmware(dsp_code);
-#endif
rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0);
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index a536c59fbea..9ff3f9e3440 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -58,10 +58,8 @@ MODULE_SUPPORTED_DEVICE("{{ESS,Maestro3 PCI},"
"{ESS,Allegro PCI},"
"{ESS,Allegro-1 PCI},"
"{ESS,Canyon3D-2/LE PCI}}");
-#ifndef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
MODULE_FIRMWARE("ess/maestro3_assp_kernel.fw");
MODULE_FIRMWARE("ess/maestro3_assp_minisrc.fw");
-#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
@@ -1177,7 +1175,8 @@ snd_m3_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
struct m3_dma *s = subs->runtime->private_data;
int err = -EINVAL;
- snd_assert(s != NULL, return -ENXIO);
+ if (snd_BUG_ON(!s))
+ return -ENXIO;
spin_lock(&chip->reg_lock);
switch (cmd) {
@@ -1489,7 +1488,8 @@ snd_m3_pcm_prepare(struct snd_pcm_substream *subs)
struct snd_pcm_runtime *runtime = subs->runtime;
struct m3_dma *s = runtime->private_data;
- snd_assert(s != NULL, return -ENXIO);
+ if (snd_BUG_ON(!s))
+ return -ENXIO;
if (runtime->format != SNDRV_PCM_FORMAT_U8 &&
runtime->format != SNDRV_PCM_FORMAT_S16_LE)
@@ -1548,7 +1548,9 @@ snd_m3_pcm_pointer(struct snd_pcm_substream *subs)
struct snd_m3 *chip = snd_pcm_substream_chip(subs);
unsigned int ptr;
struct m3_dma *s = subs->runtime->private_data;
- snd_assert(s != NULL, return 0);
+
+ if (snd_BUG_ON(!s))
+ return 0;
spin_lock(&chip->reg_lock);
ptr = snd_m3_get_pointer(chip, s, subs);
@@ -2101,163 +2103,6 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
}
-#ifdef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
-
-/*
- * DSP Code images
- */
-
-static const u16 assp_kernel_image[] = {
- 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4,
- 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
- 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
- 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4,
- 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08,
- 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909,
- 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41,
- 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308,
- 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910,
- 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00,
- 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026,
- 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0,
- 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012,
- 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A,
- 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000,
- 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05,
- 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26,
- 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300,
- 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88,
- 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008,
- 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80,
- 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71,
- 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A,
- 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100,
- 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900,
- 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903,
- 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309,
- 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B,
- 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540,
- 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C,
- 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380,
- 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340,
- 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041,
- 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18,
- 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40,
- 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9,
- 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8,
- 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A,
- 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80,
- 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40,
- 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80,
- 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A,
- 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F,
- 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1,
- 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034,
- 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E,
- 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977,
- 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B,
- 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388,
- 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80,
- 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473,
- 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473,
- 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880,
- 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80,
- 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6,
- 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909,
- 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED,
- 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005,
- 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388,
- 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4,
- 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100,
- 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10,
- 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070,
- 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71,
- 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A,
- 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061,
- 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A,
- 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6,
- 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A,
- 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6,
- 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358,
- 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6,
- 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9,
- 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379,
- 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6,
- 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C,
- 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166,
- 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B,
- 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A,
- 0xBE3A,
-};
-
-/*
- * Mini sample rate converter code image
- * that is to be loaded at 0x400 on the DSP.
- */
-static const u16 assp_minisrc_image[] = {
-
- 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412,
- 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41,
- 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907,
- 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01,
- 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904,
- 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D,
- 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD,
- 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22,
- 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0,
- 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9,
- 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89,
- 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0,
- 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484,
- 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80,
- 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624,
- 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4,
- 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027,
- 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903,
- 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809,
- 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46,
- 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47,
- 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0,
- 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516,
- 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E,
- 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C,
- 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180,
- 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E,
- 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0,
- 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0,
- 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0,
- 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F,
- 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-};
-
-static const struct firmware assp_kernel = {
- .data = (u8 *)assp_kernel_image,
- .size = sizeof assp_kernel_image
-};
-static const struct firmware assp_minisrc = {
- .data = (u8 *)assp_minisrc_image,
- .size = sizeof assp_minisrc_image
-};
-
-#else /* CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL */
-
-#ifdef __LITTLE_ENDIAN
-static inline void snd_m3_convert_from_le(const struct firmware *fw) { }
-#else
-static void snd_m3_convert_from_le(const struct firmware *fw)
-{
- int i;
- u16 *data = (u16 *)fw->data;
-
- for (i = 0; i < fw->size / 2; ++i)
- le16_to_cpus(&data[i]);
-}
-#endif
-
-#endif /* CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL */
-
-
/*
* initialize ASSP
*/
@@ -2271,7 +2116,7 @@ static const u16 minisrc_lpf[MINISRC_LPF_LEN] = {
static void snd_m3_assp_init(struct snd_m3 *chip)
{
unsigned int i;
- u16 *data;
+ const u16 *data;
/* zero kernel data */
for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
@@ -2289,10 +2134,11 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
KDATA_DMA_XFER0);
/* write kernel into code memory.. */
- data = (u16 *)chip->assp_kernel_image->data;
+ data = (const u16 *)chip->assp_kernel_image->data;
for (i = 0 ; i * 2 < chip->assp_kernel_image->size; i++) {
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
- REV_B_CODE_MEMORY_BEGIN + i, data[i]);
+ REV_B_CODE_MEMORY_BEGIN + i,
+ le16_to_cpu(data[i]));
}
/*
@@ -2301,10 +2147,10 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
* drop it there. It seems that the minisrc doesn't
* need vectors, so we won't bother with them..
*/
- data = (u16 *)chip->assp_minisrc_image->data;
+ data = (const u16 *)chip->assp_minisrc_image->data;
for (i = 0; i * 2 < chip->assp_minisrc_image->size; i++) {
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
- 0x400 + i, data[i]);
+ 0x400 + i, le16_to_cpu(data[i]));
}
/*
@@ -2427,6 +2273,29 @@ snd_m3_amp_enable(struct snd_m3 *chip, int enable)
outw(0xffff, io + GPIO_MASK);
}
+static void
+snd_m3_hv_init(struct snd_m3 *chip)
+{
+ unsigned long io = chip->iobase;
+ u16 val = GPI_VOL_DOWN | GPI_VOL_UP;
+
+ if (!chip->is_omnibook)
+ return;
+
+ /*
+ * Volume buttons on some HP OmniBook laptops
+ * require some GPIO magic to work correctly.
+ */
+ outw(0xffff, io + GPIO_MASK);
+ outw(0x0000, io + GPIO_DATA);
+
+ outw(~val, io + GPIO_MASK);
+ outw(inw(io + GPIO_DIRECTION) & ~val, io + GPIO_DIRECTION);
+ outw(val, io + GPIO_MASK);
+
+ outw(0xffff, io + GPIO_MASK);
+}
+
static int
snd_m3_chip_init(struct snd_m3 *chip)
{
@@ -2442,21 +2311,6 @@ snd_m3_chip_init(struct snd_m3 *chip)
DISABLE_LEGACY);
pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
- if (chip->is_omnibook) {
- /*
- * Volume buttons on some HP OmniBook laptops don't work
- * correctly. This makes them work for the most part.
- *
- * Volume up and down buttons on the laptop side work.
- * Fn+cursor_up (volme up) works.
- * Fn+cursor_down (volume down) doesn't work.
- * Fn+F7 (mute) works acts as volume up.
- */
- outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK);
- outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION);
- outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA);
- outw(0xffff, io + GPIO_MASK);
- }
pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
n |= chip->hv_config;
@@ -2548,10 +2402,8 @@ static int snd_m3_free(struct snd_m3 *chip)
if (chip->iobase)
pci_release_regions(chip->pci);
-#ifndef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
release_firmware(chip->assp_kernel_image);
release_firmware(chip->assp_minisrc_image);
-#endif
pci_disable_device(chip->pci);
kfree(chip);
@@ -2642,6 +2494,8 @@ static int m3_resume(struct pci_dev *pci)
snd_m3_enable_ints(chip);
snd_m3_amp_enable(chip, 1);
+ snd_m3_hv_init(chip);
+
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
@@ -2741,29 +2595,19 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
return -ENOMEM;
}
-#ifdef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
- chip->assp_kernel_image = &assp_kernel;
-#else
err = request_firmware(&chip->assp_kernel_image,
"ess/maestro3_assp_kernel.fw", &pci->dev);
if (err < 0) {
snd_m3_free(chip);
return err;
- } else
- snd_m3_convert_from_le(chip->assp_kernel_image);
-#endif
+ }
-#ifdef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
- chip->assp_minisrc_image = &assp_minisrc;
-#else
err = request_firmware(&chip->assp_minisrc_image,
"ess/maestro3_assp_minisrc.fw", &pci->dev);
if (err < 0) {
snd_m3_free(chip);
return err;
- } else
- snd_m3_convert_from_le(chip->assp_minisrc_image);
-#endif
+ }
if ((err = pci_request_regions(pci, card->driver)) < 0) {
snd_m3_free(chip);
@@ -2781,6 +2625,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
snd_m3_amp_enable(chip, 1);
+ snd_m3_hv_init(chip);
+
tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 3dd0c796327..2d0dce649a6 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -708,7 +708,7 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
pcm_number = MIXART_PCM_ANALOG;
runtime->hw = snd_mixart_analog_caps;
} else {
- snd_assert ( pcm == chip->pcm_dig );
+ snd_BUG_ON(pcm != chip->pcm_dig);
pcm_number = MIXART_PCM_DIGITAL;
runtime->hw = snd_mixart_digital_caps;
}
@@ -783,7 +783,7 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
pcm_number = MIXART_PCM_ANALOG;
runtime->hw = snd_mixart_analog_caps;
} else {
- snd_assert ( pcm == chip->pcm_dig );
+ snd_BUG_ON(pcm != chip->pcm_dig);
pcm_number = MIXART_PCM_DIGITAL;
runtime->hw = snd_mixart_digital_caps;
}
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index 785085e4835..b9a06c27939 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -56,8 +56,10 @@ static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame)
if (tailptr == headptr)
return 0; /* no message posted */
- snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */
- snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */
+ if (tailptr < MSG_OUTBOUND_POST_STACK)
+ return 0; /* error */
+ if (tailptr >= MSG_OUTBOUND_POST_STACK + MSG_BOUND_STACK_SIZE)
+ return 0; /* error */
*msg_frame = readl_be(MIXART_MEM(mgr, tailptr));
@@ -149,7 +151,8 @@ static int send_msg( struct mixart_mgr *mgr,
u32 msg_frame_address;
int err, i;
- snd_assert(msg->size % 4 == 0, return -EINVAL);
+ if (snd_BUG_ON(msg->size % 4))
+ return -EINVAL;
err = 0;
@@ -289,9 +292,12 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
wait_queue_t wait;
long timeout;
- snd_assert(notif_event != 0, return -EINVAL);
- snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL);
- snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL);
+ if (snd_BUG_ON(!notif_event))
+ return -EINVAL;
+ if (snd_BUG_ON((notif_event & MSG_TYPE_MASK) != MSG_TYPE_NOTIFY))
+ return -EINVAL;
+ if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK))
+ return -EINVAL;
mutex_lock(&mgr->msg_mutex);
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index 122c28efc48..3782b52bc0e 100644
--- a/sound/pci/mixart/mixart_hwdep.c
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -288,7 +288,9 @@ static int mixart_enum_physio(struct mixart_mgr *mgr)
return -EINVAL;
}
- snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2), return -EINVAL); /* min 2 phys io per card (analog in + analog out) */
+ /* min 2 phys io per card (analog in + analog out) */
+ if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)
+ return -EINVAL;
for(k=0; k<mgr->num_cards; k++) {
mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
@@ -363,8 +365,10 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
}
/* check xilinx validity */
- snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
- snd_assert(dsp->size % 4 == 0, return -EINVAL);
+ if (((u32*)(dsp->data))[0] == 0xffffffff)
+ return -EINVAL;
+ if (dsp->size % 4)
+ return -EINVAL;
/* set xilinx status to copying */
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
@@ -462,8 +466,10 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
}
/* check daughterboard xilinx validity */
- snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
- snd_assert(dsp->size % 4 == 0, return -EINVAL);
+ if (((u32*)(dsp->data))[0] == 0xffffffff)
+ return -EINVAL;
+ if (dsp->size % 4)
+ return -EINVAL;
/* inform mixart about the size of the file */
writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
@@ -480,7 +486,8 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* get the address where to write the file */
val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
- snd_assert(val != 0, return -EINVAL);
+ if (!val)
+ return -EINVAL;
/* copy daughterboard xilinx code */
memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size);
@@ -613,7 +620,7 @@ static int mixart_hwdep_dsp_load(struct snd_hwdep *hw,
(int)dsp->length);
return -ENOMEM;
}
- if (copy_from_user(fw.data, dsp->image, dsp->length)) {
+ if (copy_from_user((void *) fw.data, dsp->image, dsp->length)) {
vfree(fw.data);
return -EFAULT;
}
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 6fdda1f70b2..3ba6174c3df 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -837,7 +837,7 @@ static int mixart_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
} else {
- snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ snd_BUG_ON(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 */
}
@@ -863,7 +863,7 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
else /* analog capture */
stored_volume = chip->digital_capture_volume[0];
} else {
- snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
if (is_aes) /* AES playback */
stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];
else /* analog playback */
@@ -909,7 +909,7 @@ static int mixart_pcm_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
{
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
- snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
mutex_lock(&chip->mgr->mixer_mutex);
if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */
idx += MIXART_PLAYBACK_STREAMS;
@@ -926,7 +926,7 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int i, j;
- snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
mutex_lock(&chip->mgr->mixer_mutex);
j = idx;
if (is_aes)
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 7efb838d18a..50c9f8a0508 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -562,7 +562,8 @@ snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
struct nm256_stream *s = substream->runtime->private_data;
int err = 0;
- snd_assert(s != NULL, return -ENXIO);
+ if (snd_BUG_ON(!s))
+ return -ENXIO;
spin_lock(&chip->reg_lock);
switch (cmd) {
@@ -599,7 +600,8 @@ snd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd)
struct nm256_stream *s = substream->runtime->private_data;
int err = 0;
- snd_assert(s != NULL, return -ENXIO);
+ if (snd_BUG_ON(!s))
+ return -ENXIO;
spin_lock(&chip->reg_lock);
switch (cmd) {
@@ -635,7 +637,8 @@ static int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- snd_assert(s, return -ENXIO);
+ if (snd_BUG_ON(!s))
+ return -ENXIO;
s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);
s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);
s->periods = substream->runtime->periods;
@@ -660,7 +663,8 @@ snd_nm256_playback_pointer(struct snd_pcm_substream *substream)
struct nm256_stream *s = substream->runtime->private_data;
unsigned long curp;
- snd_assert(s, return 0);
+ if (snd_BUG_ON(!s))
+ return 0;
curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;
curp %= s->dma_size;
return bytes_to_frames(substream->runtime, curp);
@@ -673,7 +677,8 @@ snd_nm256_capture_pointer(struct snd_pcm_substream *substream)
struct nm256_stream *s = substream->runtime->private_data;
unsigned long curp;
- snd_assert(s != NULL, return 0);
+ if (snd_BUG_ON(!s))
+ return 0;
curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;
curp %= s->dma_size;
return bytes_to_frames(substream->runtime, curp);
@@ -1302,8 +1307,8 @@ snd_nm256_mixer(struct nm256 *chip)
.read = snd_nm256_ac97_read,
};
- chip->ac97_regs = kcalloc(sizeof(short),
- ARRAY_SIZE(nm256_ac97_init_val), GFP_KERNEL);
+ chip->ac97_regs = kcalloc(ARRAY_SIZE(nm256_ac97_init_val),
+ sizeof(short), GFP_KERNEL);
if (! chip->ac97_regs)
return -ENOMEM;
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 090dd4354a2..1ab833f843e 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/delay.h>
#include <linux/pci.h>
#include <sound/control.h>
#include <sound/core.h>
@@ -28,7 +29,7 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("TempoTec HiFier driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -62,16 +63,28 @@ static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
AK4396_WRITE | (reg << 8) | value);
}
-static void hifier_init(struct oxygen *chip)
+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 hifier_registers_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
- data->ak4396_ctl2 = AK4396_SMUTE | 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, 0);
- ak4396_write(chip, AK4396_RCH_ATT, 0);
+ update_ak4396_volume(chip);
+}
+
+static void hifier_init(struct oxygen *chip)
+{
+ struct hifier_data *data = chip->model_data;
+
+ data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ hifier_registers_init(chip);
snd_component_add(chip->card, "AK4396");
snd_component_add(chip->card, "CS5340");
@@ -81,6 +94,11 @@ static void hifier_cleanup(struct oxygen *chip)
{
}
+static void hifier_resume(struct oxygen *chip)
+{
+ hifier_registers_init(chip);
+}
+
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@@ -95,17 +113,14 @@ static void set_ak4396_params(struct oxygen *chip,
else
value |= AK4396_DFS_QUAD;
data->ak4396_ctl2 = value;
+
+ msleep(1); /* wait for the new MCLK to become stable */
+
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
ak4396_write(chip, AK4396_CONTROL_2, value);
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
}
-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;
@@ -140,15 +155,16 @@ static const struct oxygen_model model_hifier = {
.init = hifier_init,
.control_filter = hifier_control_filter,
.cleanup = hifier_cleanup,
+ .resume = hifier_resume,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct hifier_data),
- .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_1,
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_1,
.dac_channels = 2,
.dac_volume_min = 0,
.dac_volume_max = 255,
@@ -169,7 +185,7 @@ static int __devinit hifier_probe(struct pci_dev *pci,
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier);
+ err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier, 0);
if (err >= 0)
++dev;
return err;
@@ -180,6 +196,10 @@ static struct pci_driver hifier_driver = {
.id_table = hifier_ids,
.probe = hifier_probe,
.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+ .suspend = oxygen_pci_suspend,
+ .resume = oxygen_pci_resume,
+#endif
};
static int __init alsa_card_hifier_init(void)
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 63f185c1ed1..b60f6212745 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -28,6 +28,7 @@
* GPIO 1 -> DFS1 of AK5385
*/
+#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <sound/ac97_codec.h>
@@ -43,7 +44,7 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -57,17 +58,22 @@ MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
+enum {
+ MODEL_CMEDIA_REF, /* C-Media's reference design */
+ MODEL_MERIDIAN, /* AuzenTech X-Meridian */
+};
+
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) },
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
+ { OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
{ }
};
MODULE_DEVICE_TABLE(pci, oxygen_ids);
@@ -80,6 +86,7 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
struct generic_data {
u8 ak4396_ctl2;
+ u16 saved_wm8785_registers[2];
};
static void ak4396_write(struct oxygen *chip, unsigned int codec,
@@ -99,20 +106,35 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec,
static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
{
+ struct generic_data *data = chip->model_data;
+
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);
+ if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
+ data->saved_wm8785_registers[reg] = value;
}
-static void ak4396_init(struct oxygen *chip)
+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 ak4396_registers_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
unsigned int i;
- data->ak4396_ctl2 = AK4396_SMUTE | 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);
@@ -120,9 +142,16 @@ static void ak4396_init(struct oxygen *chip)
AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, i,
AK4396_CONTROL_3, AK4396_PCM);
- ak4396_write(chip, i, AK4396_LCH_ATT, 0);
- ak4396_write(chip, i, AK4396_RCH_ATT, 0);
}
+ update_ak4396_volume(chip);
+}
+
+static void ak4396_init(struct oxygen *chip)
+{
+ struct generic_data *data = chip->model_data;
+
+ data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ ak4396_registers_init(chip);
snd_component_add(chip->card, "AK4396");
}
@@ -133,12 +162,23 @@ static void ak5385_init(struct oxygen *chip)
snd_component_add(chip->card, "AK5385");
}
-static void wm8785_init(struct oxygen *chip)
+static void wm8785_registers_init(struct oxygen *chip)
{
+ struct generic_data *data = chip->model_data;
+
wm8785_write(chip, WM8785_R7, 0);
- wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE |
- WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST);
- wm8785_write(chip, WM8785_R1, WM8785_WL_24);
+ wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
+ wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
+}
+
+static void wm8785_init(struct oxygen *chip)
+{
+ struct generic_data *data = chip->model_data;
+
+ data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
+ WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
+ data->saved_wm8785_registers[1] = WM8785_WL_24;
+ wm8785_registers_init(chip);
snd_component_add(chip->card, "WM8785");
}
@@ -158,6 +198,17 @@ static void generic_cleanup(struct oxygen *chip)
{
}
+static void generic_resume(struct oxygen *chip)
+{
+ ak4396_registers_init(chip);
+ wm8785_registers_init(chip);
+}
+
+static void meridian_resume(struct oxygen *chip)
+{
+ ak4396_registers_init(chip);
+}
+
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@@ -173,6 +224,9 @@ static void set_ak4396_params(struct oxygen *chip,
else
value |= AK4396_DFS_QUAD;
data->ak4396_ctl2 = value;
+
+ msleep(1); /* wait for the new MCLK to become stable */
+
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB);
@@ -183,18 +237,6 @@ static void set_ak4396_params(struct oxygen *chip,
}
}
-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;
@@ -249,56 +291,46 @@ static void set_ak5385_params(struct oxygen *chip,
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
+static int generic_probe(struct oxygen *chip, unsigned long driver_data)
+{
+ if (driver_data == MODEL_MERIDIAN) {
+ chip->model.init = meridian_init;
+ chip->model.resume = meridian_resume;
+ chip->model.set_adc_params = set_ak5385_params;
+ chip->model.device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF;
+ chip->model.misc_flags = OXYGEN_MISC_MIDI;
+ chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
+ }
+ return 0;
+}
+
static const struct oxygen_model model_generic = {
.shortname = "C-Media CMI8788",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.owner = THIS_MODULE,
+ .probe = generic_probe,
.init = generic_init,
.cleanup = generic_cleanup,
+ .resume = generic_resume,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
- .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- PLAYBACK_2_TO_AC97_1 |
- CAPTURE_0_FROM_I2S_1 |
- CAPTURE_1_FROM_SPDIF |
- CAPTURE_2_FROM_AC97_1,
- .dac_channels = 8,
- .dac_volume_min = 0,
- .dac_volume_max = 255,
- .function_flags = OXYGEN_FUNCTION_SPI |
- OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-static const struct oxygen_model model_meridian = {
- .shortname = "C-Media CMI8788",
- .longname = "C-Media Oxygen HD Audio",
- .chip = "CMI8788",
- .owner = THIS_MODULE,
- .init = meridian_init,
- .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,
- .dac_tlv = ak4396_db_scale,
- .model_data_size = sizeof(struct generic_data),
- .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- PLAYBACK_2_TO_AC97_1 |
- CAPTURE_0_FROM_I2S_2 |
- CAPTURE_1_FROM_SPDIF |
- CAPTURE_2_FROM_AC97_1,
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ PLAYBACK_2_TO_AC97_1 |
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_1_FROM_SPDIF |
+ CAPTURE_2_FROM_AC97_1,
.dac_channels = 8,
.dac_volume_min = 0,
.dac_volume_max = 255,
- .misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
@@ -309,7 +341,6 @@ 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)
@@ -318,9 +349,8 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,
++dev;
return -ENOENT;
}
- is_meridian = pci_id->driver_data;
err = oxygen_pci_probe(pci, index[dev], id[dev],
- is_meridian ? &model_meridian : &model_generic);
+ &model_generic, pci_id->driver_data);
if (err >= 0)
++dev;
return err;
@@ -331,6 +361,10 @@ static struct pci_driver oxygen_driver = {
.id_table = oxygen_ids,
.probe = generic_oxygen_probe,
.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+ .suspend = oxygen_pci_suspend,
+ .resume = oxygen_pci_resume,
+#endif
};
static int __init alsa_card_oxygen_init(void)
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index a71c6e05926..19107c6307e 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -16,15 +16,22 @@
#define PCM_AC97 5
#define PCM_COUNT 6
+#define OXYGEN_IO_SIZE 0x100
+
/* model-specific configuration of outputs/inputs */
-#define PLAYBACK_0_TO_I2S 0x001
-#define PLAYBACK_1_TO_SPDIF 0x004
-#define PLAYBACK_2_TO_AC97_1 0x008
-#define CAPTURE_0_FROM_I2S_1 0x010
-#define CAPTURE_0_FROM_I2S_2 0x020
-#define CAPTURE_1_FROM_SPDIF 0x080
-#define CAPTURE_2_FROM_I2S_2 0x100
-#define CAPTURE_2_FROM_AC97_1 0x200
+#define PLAYBACK_0_TO_I2S 0x0001
+ /* PLAYBACK_0_TO_AC97_0 not implemented */
+#define PLAYBACK_1_TO_SPDIF 0x0004
+#define PLAYBACK_2_TO_AC97_1 0x0008
+#define CAPTURE_0_FROM_I2S_1 0x0010
+#define CAPTURE_0_FROM_I2S_2 0x0020
+ /* CAPTURE_0_FROM_AC97_0 not implemented */
+#define CAPTURE_1_FROM_SPDIF 0x0080
+#define CAPTURE_2_FROM_I2S_2 0x0100
+#define CAPTURE_2_FROM_AC97_1 0x0200
+ /* CAPTURE_3_FROM_I2S_3 not implemented */
+#define MIDI_OUTPUT 0x0800
+#define MIDI_INPUT 0x1000
enum {
CONTROL_SPDIF_PCM,
@@ -49,46 +56,20 @@ 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;
struct oxygen_model {
const char *shortname;
const char *longname;
const char *chip;
struct module *owner;
+ int (*probe)(struct oxygen *chip, unsigned long driver_data);
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 (*suspend)(struct oxygen *chip);
+ void (*resume)(struct oxygen *chip);
void (*pcm_hardware_filter)(unsigned int channel,
struct snd_pcm_hardware *hardware);
void (*set_dac_params)(struct oxygen *chip,
@@ -98,11 +79,12 @@ struct oxygen_model {
void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(struct oxygen *chip);
void (*gpio_changed)(struct oxygen *chip);
+ void (*uart_input)(struct oxygen *chip);
void (*ac97_switch)(struct oxygen *chip,
unsigned int reg, unsigned int mute);
const unsigned int *dac_tlv;
size_t model_data_size;
- unsigned int pcm_dev_cfg;
+ unsigned int device_config;
u8 dac_channels;
u8 dac_volume_min;
u8 dac_volume_max;
@@ -112,11 +94,53 @@ struct oxygen_model {
u16 adc_i2s_format;
};
+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;
+ 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;
+ union {
+ u8 _8[OXYGEN_IO_SIZE];
+ __le16 _16[OXYGEN_IO_SIZE / 2];
+ __le32 _32[OXYGEN_IO_SIZE / 4];
+ } saved_registers;
+ u16 saved_ac97_registers[2][0x40];
+ unsigned int uart_input_count;
+ u8 uart_input[32];
+ struct oxygen_model model;
+};
+
/* oxygen_lib.c */
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
- const struct oxygen_model *model);
+ const struct oxygen_model *model,
+ unsigned long driver_data);
void oxygen_pci_remove(struct pci_dev *pci);
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
+int oxygen_pci_resume(struct pci_dev *pci);
+#endif
/* oxygen_mixer.c */
@@ -153,6 +177,9 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
+void oxygen_reset_uart(struct oxygen *chip);
+void oxygen_write_uart(struct oxygen *chip, u8 data);
+
static inline void oxygen_set_bits8(struct oxygen *chip,
unsigned int reg, u8 value)
{
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 5569606ee87..3126c4b403d 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <sound/core.h>
+#include <sound/mpu401.h>
#include <asm/io.h>
#include "oxygen.h"
@@ -44,18 +45,21 @@ EXPORT_SYMBOL(oxygen_read32);
void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
{
outb(value, chip->addr + reg);
+ chip->saved_registers._8[reg] = value;
}
EXPORT_SYMBOL(oxygen_write8);
void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
{
outw(value, chip->addr + reg);
+ chip->saved_registers._16[reg / 2] = cpu_to_le16(value);
}
EXPORT_SYMBOL(oxygen_write16);
void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
{
outl(value, chip->addr + reg);
+ chip->saved_registers._32[reg / 4] = cpu_to_le32(value);
}
EXPORT_SYMBOL(oxygen_write32);
@@ -63,7 +67,10 @@ 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);
+ tmp &= ~mask;
+ tmp |= value & mask;
+ outb(tmp, chip->addr + reg);
+ chip->saved_registers._8[reg] = tmp;
}
EXPORT_SYMBOL(oxygen_write8_masked);
@@ -71,7 +78,10 @@ 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);
+ tmp &= ~mask;
+ tmp |= value & mask;
+ outw(tmp, chip->addr + reg);
+ chip->saved_registers._16[reg / 2] = cpu_to_le16(tmp);
}
EXPORT_SYMBOL(oxygen_write16_masked);
@@ -79,7 +89,10 @@ 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);
+ tmp &= ~mask;
+ tmp |= value & mask;
+ outl(tmp, chip->addr + reg);
+ chip->saved_registers._32[reg / 4] = cpu_to_le32(tmp);
}
EXPORT_SYMBOL(oxygen_write32_masked);
@@ -128,8 +141,10 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
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)
+ ++succeeded >= 2) {
+ chip->saved_ac97_registers[codec][index / 2] = data;
return;
+ }
}
snd_printk(KERN_ERR "AC'97 write timeout\n");
}
@@ -218,3 +233,24 @@ void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
device | OXYGEN_2WIRE_DIR_WRITE);
}
EXPORT_SYMBOL(oxygen_write_i2c);
+
+static void _write_uart(struct oxygen *chip, unsigned int port, u8 data)
+{
+ if (oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_TX_FULL)
+ msleep(1);
+ oxygen_write8(chip, OXYGEN_MPU401 + port, data);
+}
+
+void oxygen_reset_uart(struct oxygen *chip)
+{
+ _write_uart(chip, 1, MPU401_RESET);
+ msleep(1); /* wait for ACK */
+ _write_uart(chip, 1, MPU401_ENTER_UART);
+}
+EXPORT_SYMBOL(oxygen_reset_uart);
+
+void oxygen_write_uart(struct oxygen *chip, u8 data)
+{
+ _write_uart(chip, 0, data);
+}
+EXPORT_SYMBOL(oxygen_write_uart);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 897697d4350..84f481d41ef 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -32,9 +32,33 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 helper library");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+static inline int oxygen_uart_input_ready(struct oxygen *chip)
+{
+ return !(oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_RX_EMPTY);
+}
+
+static void oxygen_read_uart(struct oxygen *chip)
+{
+ if (unlikely(!oxygen_uart_input_ready(chip))) {
+ /* no data, but read it anyway to clear the interrupt */
+ oxygen_read8(chip, OXYGEN_MPU401);
+ return;
+ }
+ do {
+ u8 data = oxygen_read8(chip, OXYGEN_MPU401);
+ if (data == MPU401_ACK)
+ continue;
+ if (chip->uart_input_count >= ARRAY_SIZE(chip->uart_input))
+ chip->uart_input_count = 0;
+ chip->uart_input[chip->uart_input_count++] = data;
+ } while (oxygen_uart_input_ready(chip));
+ if (chip->model.uart_input)
+ chip->model.uart_input(chip);
+}
+
static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
{
struct oxygen *chip = dev_id;
@@ -87,8 +111,12 @@ static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
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_MIDI) {
+ if (chip->midi)
+ snd_mpu401_uart_interrupt(0, chip->midi->private_data);
+ else
+ oxygen_read_uart(chip);
+ }
if (status & OXYGEN_INT_AC97)
wake_up(&chip->ac97_waitqueue);
@@ -161,8 +189,8 @@ 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);
+ if (chip->model.gpio_changed)
+ chip->model.gpio_changed(chip);
}
#ifdef CONFIG_PROC_FS
@@ -173,7 +201,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
int i, j;
snd_iprintf(buffer, "CMI8788\n\n");
- for (i = 0; i < 0x100; i += 0x10) {
+ for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; ++j)
snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
@@ -221,7 +249,7 @@ static void oxygen_init(struct oxygen *chip)
chip->dac_routing = 1;
for (i = 0; i < 8; ++i)
- chip->dac_volume[i] = chip->model->dac_volume_min;
+ chip->dac_volume[i] = chip->model.dac_volume_min;
chip->dac_mute = 1;
chip->spdif_playback_enable = 1;
chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
@@ -243,7 +271,7 @@ static void oxygen_init(struct oxygen *chip)
oxygen_write8_masked(chip, OXYGEN_FUNCTION,
OXYGEN_FUNCTION_RESET_CODEC |
- chip->model->function_flags,
+ chip->model.function_flags,
OXYGEN_FUNCTION_RESET_CODEC |
OXYGEN_FUNCTION_2WIRE_SPI_MASK |
OXYGEN_FUNCTION_ENABLE_SPI_4_5);
@@ -255,7 +283,7 @@ static void oxygen_init(struct oxygen *chip)
OXYGEN_DMA_MULTICH_BURST_8);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
oxygen_write8_masked(chip, OXYGEN_MISC,
- chip->model->misc_flags,
+ chip->model.misc_flags,
OXYGEN_MISC_WRITE_PCI_SUBID |
OXYGEN_MISC_REC_C_FROM_SPDIF |
OXYGEN_MISC_REC_B_FROM_AC97 |
@@ -270,21 +298,21 @@ static void oxygen_init(struct oxygen *chip)
(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 | chip->model->dac_i2s_format |
+ OXYGEN_RATE_48000 | chip->model.dac_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+ if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
- OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+ OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
else
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
- if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 |
- CAPTURE_2_FROM_I2S_2))
+ if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_2_FROM_I2S_2))
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
- OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+ OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
else
@@ -295,7 +323,7 @@ static void oxygen_init(struct oxygen *chip)
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE |
OXYGEN_SPDIF_LOOPBACK);
- if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+ if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_SENSE_MASK |
OXYGEN_SPDIF_LOCK_MASK |
@@ -314,6 +342,10 @@ static void oxygen_init(struct oxygen *chip)
OXYGEN_SPDIF_LOCK_MASK |
OXYGEN_SPDIF_RATE_MASK);
oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_STANDARD);
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);
@@ -413,14 +445,15 @@ static void oxygen_card_free(struct snd_card *card)
if (chip->irq >= 0)
free_irq(chip->irq, chip);
flush_scheduled_work();
- chip->model->cleanup(chip);
+ chip->model.cleanup(chip);
mutex_destroy(&chip->mutex);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
}
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
- const struct oxygen_model *model)
+ const struct oxygen_model *model,
+ unsigned long driver_data)
{
struct snd_card *card;
struct oxygen *chip;
@@ -435,7 +468,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- chip->model = model;
+ chip->model = *model;
chip->model_data = chip + 1;
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->mutex);
@@ -455,7 +488,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
}
if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
- pci_resource_len(pci, 0) < 0x100) {
+ pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
snd_printk(KERN_ERR "invalid PCI I/O range\n");
err = -ENXIO;
goto err_pci_regions;
@@ -466,23 +499,28 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free;
+ if (chip->model.probe) {
+ err = chip->model.probe(chip, driver_data);
+ if (err < 0)
+ goto err_card;
+ }
oxygen_init(chip);
- model->init(chip);
+ chip->model.init(chip);
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
- model->chip, chip);
+ chip->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);
+ strcpy(card->driver, chip->model.chip);
+ strcpy(card->shortname, chip->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);
+ chip->model.longname, chip->revision, chip->addr, chip->irq);
+ strcpy(card->mixername, chip->model.chip);
+ snd_component_add(card, chip->model.chip);
err = oxygen_pcm_init(chip);
if (err < 0)
@@ -492,10 +530,15 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
if (err < 0)
goto err_card;
- if (model->misc_flags & OXYGEN_MISC_MIDI) {
+ if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) {
+ unsigned int info_flags = MPU401_INFO_INTEGRATED;
+ if (chip->model.device_config & MIDI_OUTPUT)
+ info_flags |= MPU401_INFO_OUTPUT;
+ if (chip->model.device_config & MIDI_INPUT)
+ info_flags |= MPU401_INFO_INPUT;
err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
chip->addr + OXYGEN_MPU401,
- MPU401_INFO_INTEGRATED, 0, 0,
+ info_flags, 0, 0,
&chip->midi);
if (err < 0)
goto err_card;
@@ -504,7 +547,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
oxygen_proc_init(chip);
spin_lock_irq(&chip->reg_lock);
- if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+ if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
if (chip->has_ac97_0 | chip->has_ac97_1)
chip->interrupt_mask |= OXYGEN_INT_AC97;
@@ -534,3 +577,99 @@ void oxygen_pci_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
EXPORT_SYMBOL(oxygen_pci_remove);
+
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct oxygen *chip = card->private_data;
+ unsigned int i, saved_interrupt_mask;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ for (i = 0; i < PCM_COUNT; ++i)
+ if (chip->streams[i])
+ snd_pcm_suspend(chip->streams[i]);
+
+ if (chip->model.suspend)
+ chip->model.suspend(chip);
+
+ spin_lock_irq(&chip->reg_lock);
+ saved_interrupt_mask = chip->interrupt_mask;
+ chip->interrupt_mask = 0;
+ oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+ spin_unlock_irq(&chip->reg_lock);
+
+ synchronize_irq(chip->irq);
+ flush_scheduled_work();
+ chip->interrupt_mask = saved_interrupt_mask;
+
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+ return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_suspend);
+
+static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {
+ 0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff,
+ 0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000
+};
+static const u32 ac97_registers_to_restore[2][0x40 / 32] = {
+ { 0x18284fa2, 0x03060000 },
+ { 0x00007fa6, 0x00200000 }
+};
+
+static inline int is_bit_set(const u32 *bitmap, unsigned int bit)
+{
+ return bitmap[bit / 32] & (1 << (bit & 31));
+}
+
+static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
+{
+ unsigned int i;
+
+ oxygen_write_ac97(chip, codec, AC97_RESET, 0);
+ msleep(1);
+ for (i = 1; i < 0x40; ++i)
+ if (is_bit_set(ac97_registers_to_restore[codec], i))
+ oxygen_write_ac97(chip, codec, i * 2,
+ chip->saved_ac97_registers[codec][i]);
+}
+
+int oxygen_pci_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct oxygen *chip = card->private_data;
+ unsigned int i;
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+ if (pci_enable_device(pci) < 0) {
+ snd_printk(KERN_ERR "cannot reenable device");
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+ pci_set_master(pci);
+
+ oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+ for (i = 0; i < OXYGEN_IO_SIZE; ++i)
+ if (is_bit_set(registers_to_restore, i))
+ oxygen_write8(chip, i, chip->saved_registers._8[i]);
+ if (chip->has_ac97_0)
+ oxygen_restore_ac97(chip, 0);
+ if (chip->has_ac97_1)
+ oxygen_restore_ac97(chip, 1);
+
+ if (chip->model.resume)
+ chip->model.resume(chip);
+
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_resume);
+#endif /* CONFIG_PM */
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 6facac5aed9..304da169bfd 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -31,9 +31,9 @@ static int dac_volume_info(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = chip->model->dac_channels;
- info->value.integer.min = chip->model->dac_volume_min;
- info->value.integer.max = chip->model->dac_volume_max;
+ info->count = chip->model.dac_channels;
+ info->value.integer.min = chip->model.dac_volume_min;
+ info->value.integer.max = chip->model.dac_volume_max;
return 0;
}
@@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,
unsigned int i;
mutex_lock(&chip->mutex);
- for (i = 0; i < chip->model->dac_channels; ++i)
+ for (i = 0; i < chip->model.dac_channels; ++i)
value->value.integer.value[i] = chip->dac_volume[i];
mutex_unlock(&chip->mutex);
return 0;
@@ -59,13 +59,13 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
changed = 0;
mutex_lock(&chip->mutex);
- for (i = 0; i < chip->model->dac_channels; ++i)
+ 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);
+ chip->model.update_dac_volume(chip);
mutex_unlock(&chip->mutex);
return changed;
}
@@ -91,7 +91,7 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
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);
+ chip->model.update_dac_mute(chip);
}
mutex_unlock(&chip->mutex);
return changed;
@@ -103,7 +103,7 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"Front", "Front+Surround", "Front+Surround+Back"
};
struct oxygen *chip = ctl->private_data;
- unsigned int count = 2 + (chip->model->dac_channels == 8);
+ unsigned int count = 2 + (chip->model.dac_channels == 8);
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
@@ -172,7 +172,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
- unsigned int count = 2 + (chip->model->dac_channels == 8);
+ unsigned int count = 2 + (chip->model.dac_channels == 8);
int changed;
mutex_lock(&chip->mutex);
@@ -211,13 +211,13 @@ static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate)
case OXYGEN_RATE_64000:
return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT;
case OXYGEN_RATE_88200:
- return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ return IEC958_AES3_CON_FS_88200 << OXYGEN_SPDIF_CS_RATE_SHIFT;
case OXYGEN_RATE_96000:
- return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ return IEC958_AES3_CON_FS_96000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
case OXYGEN_RATE_176400:
- return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ return IEC958_AES3_CON_FS_176400 << OXYGEN_SPDIF_CS_RATE_SHIFT;
case OXYGEN_RATE_192000:
- return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ return IEC958_AES3_CON_FS_192000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
}
}
@@ -512,14 +512,17 @@ static int ac97_switch_get(struct snd_kcontrol *ctl,
static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
{
- unsigned int priv_idx = chip->controls[control]->private_value & 0xff;
+ unsigned int priv_idx;
u16 value;
+ if (!chip->controls[control])
+ return;
+ priv_idx = chip->controls[control]->private_value & 0xff;
value = oxygen_read_ac97(chip, 0, priv_idx);
if (!(value & 0x8000)) {
oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000);
- if (chip->model->ac97_switch)
- chip->model->ac97_switch(chip, priv_idx, 0x8000);
+ if (chip->model.ac97_switch)
+ chip->model.ac97_switch(chip, priv_idx, 0x8000);
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->controls[control]->id);
}
@@ -546,8 +549,8 @@ static int ac97_switch_put(struct snd_kcontrol *ctl,
change = newreg != oldreg;
if (change) {
oxygen_write_ac97(chip, codec, index, newreg);
- if (codec == 0 && chip->model->ac97_switch)
- chip->model->ac97_switch(chip, index, newreg & 0x8000);
+ if (codec == 0 && chip->model.ac97_switch)
+ chip->model.ac97_switch(chip, index, newreg & 0x8000);
if (index == AC97_LINE) {
oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
newreg & 0x8000 ?
@@ -936,16 +939,16 @@ static int add_controls(struct oxygen *chip,
for (i = 0; i < count; ++i) {
template = controls[i];
- if (chip->model->control_filter) {
- err = chip->model->control_filter(&template);
+ if (chip->model.control_filter) {
+ err = chip->model.control_filter(&template);
if (err < 0)
return err;
if (err == 1)
continue;
}
if (!strcmp(template.name, "Master Playback Volume") &&
- chip->model->dac_tlv) {
- template.tlv.p = chip->model->dac_tlv;
+ chip->model.dac_tlv) {
+ template.tlv.p = chip->model.dac_tlv;
template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
}
ctl = snd_ctl_new1(&template, chip);
@@ -971,14 +974,14 @@ int oxygen_mixer_init(struct oxygen *chip)
err = add_controls(chip, controls, ARRAY_SIZE(controls));
if (err < 0)
return err;
- if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) {
+ if (chip->model.device_config & CAPTURE_1_FROM_SPDIF) {
err = add_controls(chip, spdif_input_controls,
ARRAY_SIZE(spdif_input_controls));
if (err < 0)
return err;
}
for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) {
- if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev))
+ if (!(chip->model.device_config & monitor_controls[i].pcm_dev))
continue;
err = add_controls(chip, monitor_controls[i].controls,
ARRAY_SIZE(monitor_controls[i].controls));
@@ -997,5 +1000,5 @@ int oxygen_mixer_init(struct oxygen *chip)
if (err < 0)
return err;
}
- return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0;
+ 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
index b17c405e069..c262049961e 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -24,6 +24,16 @@
#include <sound/pcm_params.h>
#include "oxygen.h"
+/* most DMA channels have a 16-bit counter for 32-bit words */
+#define BUFFER_BYTES_MAX ((1 << 16) * 4)
+/* the multichannel DMA channel has a 24-bit counter */
+#define BUFFER_BYTES_MAX_MULTICH ((1 << 24) * 4)
+
+#define PERIOD_BYTES_MIN 64
+
+#define DEFAULT_BUFFER_BYTES (BUFFER_BYTES_MAX / 2)
+#define DEFAULT_BUFFER_BYTES_MULTICH (1024 * 1024)
+
static const struct snd_pcm_hardware oxygen_stereo_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -44,11 +54,11 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
.rate_max = 192000,
.channels_min = 2,
.channels_max = 2,
- .buffer_bytes_max = 256 * 1024,
- .period_bytes_min = 128,
- .period_bytes_max = 128 * 1024,
+ .buffer_bytes_max = BUFFER_BYTES_MAX,
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = BUFFER_BYTES_MAX / 2,
.periods_min = 2,
- .periods_max = 2048,
+ .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
@@ -70,11 +80,11 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
.rate_max = 192000,
.channels_min = 2,
.channels_max = 8,
- .buffer_bytes_max = 2048 * 1024,
- .period_bytes_min = 128,
- .period_bytes_max = 256 * 1024,
+ .buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2,
.periods_min = 2,
- .periods_max = 16384,
+ .periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware oxygen_ac97_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
@@ -88,11 +98,11 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
- .buffer_bytes_max = 256 * 1024,
- .period_bytes_min = 128,
- .period_bytes_max = 128 * 1024,
+ .buffer_bytes_max = BUFFER_BYTES_MAX,
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = BUFFER_BYTES_MAX / 2,
.periods_min = 2,
- .periods_max = 2048,
+ .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
@@ -119,7 +129,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
runtime->private_data = (void *)(uintptr_t)channel;
if (channel == PCM_B && chip->has_ac97_1 &&
- (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1))
+ (chip->model.device_config & CAPTURE_2_FROM_AC97_1))
runtime->hw = oxygen_ac97_hardware;
else
runtime->hw = *oxygen_hardware[channel];
@@ -130,11 +140,11 @@ static int oxygen_open(struct snd_pcm_substream *substream,
runtime->hw.rate_min = 44100;
break;
case PCM_MULTICH:
- runtime->hw.channels_max = chip->model->dac_channels;
+ runtime->hw.channels_max = chip->model.dac_channels;
break;
}
- if (chip->model->pcm_hardware_filter)
- chip->model->pcm_hardware_filter(channel, &runtime->hw);
+ 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)
@@ -155,6 +165,12 @@ static int oxygen_open(struct snd_pcm_substream *substream,
if (err < 0)
return err;
}
+ if (channel == PCM_MULTICH) {
+ err = snd_pcm_hw_constraint_minmax
+ (runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000);
+ if (err < 0)
+ return err;
+ }
snd_pcm_set_sync(substream);
chip->streams[channel] = substream;
@@ -339,7 +355,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) |
oxygen_i2s_mclk(hw_params) |
- chip->model->adc_i2s_format |
+ chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@@ -348,7 +364,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
spin_unlock_irq(&chip->reg_lock);
mutex_lock(&chip->mutex);
- chip->model->set_adc_params(chip, hw_params);
+ chip->model.set_adc_params(chip, hw_params);
mutex_unlock(&chip->mutex);
return 0;
}
@@ -365,7 +381,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
return err;
is_ac97 = chip->has_ac97_1 &&
- (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+ (chip->model.device_config & CAPTURE_2_FROM_AC97_1);
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
@@ -375,7 +391,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) |
oxygen_i2s_mclk(hw_params) |
- chip->model->adc_i2s_format |
+ chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@@ -385,7 +401,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
if (!is_ac97) {
mutex_lock(&chip->mutex);
- chip->model->set_adc_params(chip, hw_params);
+ chip->model.set_adc_params(chip, hw_params);
mutex_unlock(&chip->mutex);
}
return 0;
@@ -452,7 +468,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
OXYGEN_MULTICH_FORMAT_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) |
- chip->model->dac_i2s_format |
+ chip->model.dac_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@@ -462,7 +478,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
spin_unlock_irq(&chip->reg_lock);
mutex_lock(&chip->mutex);
- chip->model->set_dac_params(chip, hw_params);
+ chip->model.set_dac_params(chip, hw_params);
mutex_unlock(&chip->mutex);
return 0;
}
@@ -517,6 +533,7 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
pausing = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -640,39 +657,42 @@ int oxygen_pcm_init(struct oxygen *chip)
int outs, ins;
int err;
- outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S);
- ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 |
- CAPTURE_0_FROM_I2S_2));
+ outs = !!(chip->model.device_config & PLAYBACK_0_TO_I2S);
+ ins = !!(chip->model.device_config & (CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_0_FROM_I2S_2));
if (outs | ins) {
- err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
+ err = snd_pcm_new(chip->card, "Multichannel",
+ 0, outs, ins, &pcm);
if (err < 0)
return err;
if (outs)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&oxygen_multich_ops);
- if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+ if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_a_ops);
- else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2)
+ else if (chip->model.device_config & CAPTURE_0_FROM_I2S_2)
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");
+ strcpy(pcm->name, "Multichannel");
if (outs)
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);
+ DEFAULT_BUFFER_BYTES_MULTICH,
+ BUFFER_BYTES_MAX_MULTICH);
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);
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
}
- outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
- ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF);
+ outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF);
+ ins = !!(chip->model.device_config & CAPTURE_1_FROM_SPDIF);
if (outs | ins) {
err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
if (err < 0)
@@ -688,15 +708,16 @@ int oxygen_pcm_init(struct oxygen *chip)
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);
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
}
if (chip->has_ac97_1) {
- outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1);
- ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+ outs = !!(chip->model.device_config & PLAYBACK_2_TO_AC97_1);
+ ins = !!(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
} else {
outs = 0;
- ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2);
+ ins = !!(chip->model.device_config & CAPTURE_2_FROM_I2S_2);
}
if (outs | ins) {
err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
@@ -718,7 +739,8 @@ int oxygen_pcm_init(struct oxygen *chip)
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);
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
}
return 0;
}
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 7f84fa5deca..98c6a8c65d8 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -36,15 +36,15 @@
*/
/*
- * Xonar DX
- * --------
+ * Xonar D1/DX
+ * -----------
*
* CMI8788:
*
* I²C <-> CS4398 (front)
* <-> CS4362A (surround, center/LFE, back)
*
- * GPI 0 <- external power present
+ * GPI 0 <- external power present (DX only)
*
* GPIO 0 -> enable output to speakers
* GPIO 1 -> enable front panel I/O
@@ -62,14 +62,66 @@
* AD0 <- 0
*/
+/*
+ * Xonar HDAV1.3 (Deluxe)
+ * ----------------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1796 (front)
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * TXD -> HDMI controller
+ * RXD <- HDMI controller
+ *
+ * PCM1796 front: AD1,0 <- 0,0
+ *
+ * no daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 1
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ *
+ * I²C <-> PCM1796 (surround)
+ * <-> PCM1796 (center/LFE)
+ * <-> PCM1796 (back)
+ *
+ * PCM1796 surround: AD1,0 <- 0,1
+ * PCM1796 center/LFE: AD1,0 <- 1,0
+ * PCM1796 back: AD1,0 <- 1,1
+ *
+ * unknown daughterboard
+ * ---------------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 1
+ *
+ * I²C <-> CS4362A (surround, center/LFE, back)
+ *
+ * CS4362A: AD0 <- 0
+ */
+
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "cm9780.h"
@@ -79,7 +131,7 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Asus AVx00 driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -96,13 +148,18 @@ MODULE_PARM_DESC(enable, "enable card");
enum {
MODEL_D2,
MODEL_D2X,
+ MODEL_D1,
MODEL_DX,
+ MODEL_HDAV, /* without daughterboard */
+ MODEL_HDAV_H6, /* with H6 daughterboard */
};
static struct pci_device_id xonar_ids[] __devinitdata = {
{ OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
{ OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
+ { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
{ }
};
MODULE_DEVICE_TABLE(pci, xonar_ids);
@@ -122,20 +179,33 @@ MODULE_DEVICE_TABLE(pci, xonar_ids);
#define GPIO_DX_FRONT_PANEL 0x0002
#define GPIO_DX_INPUT_ROUTE 0x0100
+#define GPIO_HDAV_DB_MASK 0x0030
+#define GPIO_HDAV_DB_H6 0x0000
+#define GPIO_HDAV_DB_XX 0x0020
+
+#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
struct xonar_data {
+ unsigned int model;
unsigned int anti_pop_delay;
+ unsigned int dacs;
u16 output_enable_bit;
u8 ext_power_reg;
u8 ext_power_int_reg;
u8 ext_power_bit;
u8 has_power;
+ u8 pcm1796_oversampling;
+ u8 cs4398_fm;
+ u8 cs4362a_fm;
+ u8 hdmi_params[5];
};
-static void pcm1796_write(struct oxygen *chip, unsigned int codec,
- u8 reg, u8 value)
+static void xonar_gpio_changed(struct oxygen *chip);
+
+static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
{
/* maps ALSA channel pair number to SPI output */
static const u8 codec_map[4] = {
@@ -149,6 +219,22 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec,
(reg << 8) | value);
}
+static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value);
+}
+
+static void pcm1796_write(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
+ OXYGEN_FUNCTION_SPI)
+ pcm1796_write_spi(chip, codec, reg, value);
+ else
+ pcm1796_write_i2c(chip, codec, reg, value);
+}
+
static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
{
oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
@@ -159,6 +245,32 @@ static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
}
+static void hdmi_write_command(struct oxygen *chip, u8 command,
+ unsigned int count, const u8 *params)
+{
+ unsigned int i;
+ u8 checksum;
+
+ oxygen_write_uart(chip, 0xfb);
+ oxygen_write_uart(chip, 0xef);
+ oxygen_write_uart(chip, command);
+ oxygen_write_uart(chip, count);
+ for (i = 0; i < count; ++i)
+ oxygen_write_uart(chip, params[i]);
+ checksum = 0xfb + 0xef + command + count;
+ for (i = 0; i < count; ++i)
+ checksum += params[i];
+ oxygen_write_uart(chip, checksum);
+}
+
+static void xonar_enable_output(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ msleep(data->anti_pop_delay);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
static void xonar_common_init(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
@@ -167,35 +279,72 @@ static void xonar_common_init(struct oxygen *chip)
oxygen_set_bits8(chip, data->ext_power_int_reg,
data->ext_power_bit);
chip->interrupt_mask |= OXYGEN_INT_GPIO;
+ chip->model.gpio_changed = xonar_gpio_changed;
data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
& data->ext_power_bit);
}
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_CS53x1_M_MASK | data->output_enable_bit);
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
- msleep(data->anti_pop_delay);
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
- oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+ xonar_enable_output(chip);
}
-static void xonar_d2_init(struct oxygen *chip)
+static void update_pcm1796_volume(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
unsigned int i;
- data->anti_pop_delay = 300;
- data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+ for (i = 0; i < data->dacs; ++i) {
+ pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
+ pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
+ }
+}
+
+static void update_pcm1796_mute(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+ unsigned int i;
+ u8 value;
- for (i = 0; i < 4; ++i) {
- pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED |
- PCM1796_FMT_24_LJUST | PCM1796_ATLD);
+ value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+ if (chip->dac_mute)
+ value |= PCM1796_MUTE;
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write(chip, i, 18, value);
+}
+
+static void pcm1796_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+ unsigned int i;
+
+ for (i = 0; i < data->dacs; ++i) {
pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
- pcm1796_write(chip, i, 20, PCM1796_OS_64);
+ pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
pcm1796_write(chip, i, 21, 0);
- pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */
- pcm1796_write(chip, i, 17, 0x0f);
}
+ update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */
+ update_pcm1796_volume(chip);
+}
+
+static void xonar_d2_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->anti_pop_delay = 300;
+ data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+ data->pcm1796_oversampling = PCM1796_OS_64;
+ if (data->model == MODEL_D2X) {
+ data->ext_power_reg = OXYGEN_GPIO_DATA;
+ data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+ data->ext_power_bit = GPIO_D2X_EXT_POWER;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_D2X_EXT_POWER);
+ }
+
+ pcm1796_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
@@ -206,42 +355,47 @@ static void xonar_d2_init(struct oxygen *chip)
snd_component_add(chip->card, "CS5381");
}
-static void xonar_d2x_init(struct oxygen *chip)
+static void update_cs4362a_volumes(struct oxygen *chip)
{
- struct xonar_data *data = chip->model_data;
+ u8 mute;
- data->ext_power_reg = OXYGEN_GPIO_DATA;
- data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
- data->ext_power_bit = GPIO_D2X_EXT_POWER;
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
- xonar_d2_init(chip);
+ mute = chip->dac_mute ? CS4362A_MUTE : 0;
+ cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
+ cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
+ cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
+ cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
+ cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
+ cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
}
-static void xonar_dx_init(struct oxygen *chip)
+static void update_cs43xx_volume(struct oxygen *chip)
{
- struct xonar_data *data = chip->model_data;
+ cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
+ cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
+ update_cs4362a_volumes(chip);
+}
- data->anti_pop_delay = 800;
- data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
- data->ext_power_reg = OXYGEN_GPI_DATA;
- data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
- data->ext_power_bit = GPI_DX_EXT_POWER;
+static void update_cs43xx_mute(struct oxygen *chip)
+{
+ u8 reg;
- oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
- OXYGEN_2WIRE_LENGTH_8 |
- OXYGEN_2WIRE_INTERRUPT_MASK |
- OXYGEN_2WIRE_SPEED_FAST);
+ reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
+ if (chip->dac_mute)
+ reg |= CS4398_MUTE_B | CS4398_MUTE_A;
+ cs4398_write(chip, 4, reg);
+ update_cs4362a_volumes(chip);
+}
+
+static void cs43xx_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
/* set CPEN (control port mode) and power down */
cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
/* configure */
- cs4398_write(chip, 2, CS4398_FM_SINGLE |
- CS4398_DEM_NONE | CS4398_DIF_LJUST);
+ cs4398_write(chip, 2, data->cs4398_fm);
cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
- cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE);
- cs4398_write(chip, 5, 0xfe);
- cs4398_write(chip, 6, 0xfe);
cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
@@ -249,21 +403,37 @@ static void xonar_dx_init(struct oxygen *chip)
CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
cs4362a_write(chip, 0x05, 0);
- cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE |
- CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
- cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE);
- cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE);
- cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE |
- CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
- cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE);
- cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE);
- cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE |
- CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
- cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE);
- cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x06, data->cs4362a_fm);
+ cs4362a_write(chip, 0x09, data->cs4362a_fm);
+ cs4362a_write(chip, 0x0c, data->cs4362a_fm);
+ update_cs43xx_volume(chip);
+ update_cs43xx_mute(chip);
/* clear power down */
cs4398_write(chip, 8, CS4398_CPEN);
cs4362a_write(chip, 0x01, CS4362A_CPEN);
+}
+
+static void xonar_d1_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->anti_pop_delay = 800;
+ data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
+ data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
+ data->cs4362a_fm = CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+ if (data->model == MODEL_DX) {
+ data->ext_power_reg = OXYGEN_GPI_DATA;
+ data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->ext_power_bit = GPI_DX_EXT_POWER;
+ }
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ cs43xx_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
@@ -277,51 +447,135 @@ static void xonar_dx_init(struct oxygen *chip)
snd_component_add(chip->card, "CS5361");
}
-static void xonar_cleanup(struct oxygen *chip)
+static void xonar_hdav_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+ u8 param;
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ data->anti_pop_delay = 100;
+ data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
+ data->ext_power_reg = OXYGEN_GPI_DATA;
+ data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->ext_power_bit = GPI_DX_EXT_POWER;
+ data->pcm1796_oversampling = PCM1796_OS_64;
+
+ pcm1796_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE);
+
+ oxygen_reset_uart(chip);
+ param = 0;
+ hdmi_write_command(chip, 0x61, 1, &param);
+ param = 1;
+ hdmi_write_command(chip, 0x74, 1, &param);
+ data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
+ data->hdmi_params[4] = 1;
+ hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
+
+ xonar_common_init(chip);
+
+ snd_component_add(chip->card, "PCM1796");
+ snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_disable_output(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
}
-static void xonar_dx_cleanup(struct oxygen *chip)
+static void xonar_d2_cleanup(struct oxygen *chip)
{
- xonar_cleanup(chip);
+ xonar_disable_output(chip);
+}
+
+static void xonar_d1_cleanup(struct oxygen *chip)
+{
+ xonar_disable_output(chip);
cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
}
-static void set_pcm1796_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
+static void xonar_hdav_cleanup(struct oxygen *chip)
{
- unsigned int i;
- u8 value;
+ u8 param = 0;
- value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
- for (i = 0; i < 4; ++i)
- pcm1796_write(chip, i, 20, value);
+ hdmi_write_command(chip, 0x74, 1, &param);
+ xonar_disable_output(chip);
}
-static void update_pcm1796_volume(struct oxygen *chip)
+static void xonar_d2_suspend(struct oxygen *chip)
{
- unsigned int i;
+ xonar_d2_cleanup(chip);
+}
- 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 xonar_d1_suspend(struct oxygen *chip)
+{
+ xonar_d1_cleanup(chip);
+}
+
+static void xonar_hdav_suspend(struct oxygen *chip)
+{
+ xonar_hdav_cleanup(chip);
+ msleep(2);
+}
+
+static void xonar_d2_resume(struct oxygen *chip)
+{
+ pcm1796_init(chip);
+ xonar_enable_output(chip);
+}
+
+static void xonar_d1_resume(struct oxygen *chip)
+{
+ cs43xx_init(chip);
+ xonar_enable_output(chip);
+}
+
+static void xonar_hdav_resume(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+ u8 param;
+
+ oxygen_reset_uart(chip);
+ param = 0;
+ hdmi_write_command(chip, 0x61, 1, &param);
+ param = 1;
+ hdmi_write_command(chip, 0x74, 1, &param);
+ hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
+ pcm1796_init(chip);
+ xonar_enable_output(chip);
+}
+
+static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
+ struct snd_pcm_hardware *hardware)
+{
+ if (channel == PCM_MULTICH) {
+ hardware->rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000;
+ hardware->rate_min = 44100;
}
}
-static void update_pcm1796_mute(struct oxygen *chip)
+static void set_pcm1796_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
{
+ struct xonar_data *data = chip->model_data;
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);
+ data->pcm1796_oversampling =
+ params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
}
static void set_cs53x1_params(struct oxygen *chip,
@@ -342,55 +596,60 @@ static void set_cs53x1_params(struct oxygen *chip,
static void set_cs43xx_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
- u8 fm_cs4398, fm_cs4362a;
+ struct xonar_data *data = chip->model_data;
- fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST;
- fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+ data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST;
+ data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
if (params_rate(params) <= 50000) {
- fm_cs4398 |= CS4398_FM_SINGLE;
- fm_cs4362a |= CS4362A_FM_SINGLE;
+ data->cs4398_fm |= CS4398_FM_SINGLE;
+ data->cs4362a_fm |= CS4362A_FM_SINGLE;
} else if (params_rate(params) <= 100000) {
- fm_cs4398 |= CS4398_FM_DOUBLE;
- fm_cs4362a |= CS4362A_FM_DOUBLE;
+ data->cs4398_fm |= CS4398_FM_DOUBLE;
+ data->cs4362a_fm |= CS4362A_FM_DOUBLE;
} else {
- fm_cs4398 |= CS4398_FM_QUAD;
- fm_cs4362a |= CS4362A_FM_QUAD;
+ data->cs4398_fm |= CS4398_FM_QUAD;
+ data->cs4362a_fm |= CS4362A_FM_QUAD;
}
- cs4398_write(chip, 2, fm_cs4398);
- cs4362a_write(chip, 0x06, fm_cs4362a);
- cs4362a_write(chip, 0x09, fm_cs4362a);
- cs4362a_write(chip, 0x0c, fm_cs4362a);
+ cs4398_write(chip, 2, data->cs4398_fm);
+ cs4362a_write(chip, 0x06, data->cs4362a_fm);
+ cs4362a_write(chip, 0x09, data->cs4362a_fm);
+ cs4362a_write(chip, 0x0c, data->cs4362a_fm);
}
-static void update_cs4362a_volumes(struct oxygen *chip)
+static void set_hdmi_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
{
- u8 mute;
-
- mute = chip->dac_mute ? CS4362A_MUTE : 0;
- cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
- cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
- cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
- cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
- cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
- cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
-}
+ struct xonar_data *data = chip->model_data;
-static void update_cs43xx_volume(struct oxygen *chip)
-{
- cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
- cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
- update_cs4362a_volumes(chip);
+ data->hdmi_params[0] = 0; /* 1 = non-audio */
+ switch (params_rate(params)) {
+ case 44100:
+ data->hdmi_params[1] = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
+ break;
+ default: /* 96000 */
+ data->hdmi_params[1] = IEC958_AES3_CON_FS_96000;
+ break;
+ case 192000:
+ data->hdmi_params[1] = IEC958_AES3_CON_FS_192000;
+ break;
+ }
+ data->hdmi_params[2] = params_channels(params) / 2 - 1;
+ if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+ data->hdmi_params[3] = 0;
+ else
+ data->hdmi_params[3] = 0xc0;
+ data->hdmi_params[4] = 1; /* ? */
+ hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
}
-static void update_cs43xx_mute(struct oxygen *chip)
+static void set_hdav_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
{
- u8 reg;
-
- reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
- if (chip->dac_mute)
- reg |= CS4398_MUTE_B | CS4398_MUTE_A;
- cs4398_write(chip, 4, reg);
- update_cs4362a_volumes(chip);
+ set_pcm1796_params(chip, params);
+ set_hdmi_params(chip, params);
}
static void xonar_gpio_changed(struct oxygen *chip)
@@ -412,29 +671,43 @@ static void xonar_gpio_changed(struct oxygen *chip)
}
}
-static int alt_switch_get(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
+static void xonar_hdav_uart_input(struct oxygen *chip)
+{
+ if (chip->uart_input_count >= 2 &&
+ chip->uart_input[chip->uart_input_count - 2] == 'O' &&
+ chip->uart_input[chip->uart_input_count - 1] == 'K') {
+ printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:");
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+ chip->uart_input, chip->uart_input_count);
+ chip->uart_input_count = 0;
+ }
+}
+
+static int gpio_bit_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
+ u16 bit = ctl->private_value;
value->value.integer.value[0] =
- !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT);
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
return 0;
}
-static int alt_switch_put(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
+static int gpio_bit_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
+ u16 bit = ctl->private_value;
u16 old_bits, new_bits;
int changed;
spin_lock_irq(&chip->reg_lock);
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
if (value->value.integer.value[0])
- new_bits = old_bits | GPIO_D2_ALT;
+ new_bits = old_bits | bit;
else
- new_bits = old_bits & ~GPIO_D2_ALT;
+ new_bits = old_bits & ~bit;
changed = new_bits != old_bits;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
@@ -446,47 +719,22 @@ 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,
+ .get = gpio_bit_switch_get,
+ .put = gpio_bit_switch_put,
+ .private_value = GPIO_D2_ALT,
};
-static int front_panel_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_DX_FRONT_PANEL);
- return 0;
-}
-
-static int front_panel_put(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
-{
- struct oxygen *chip = ctl->private_data;
- u16 old_reg, new_reg;
-
- spin_lock_irq(&chip->reg_lock);
- old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
- if (value->value.integer.value[0])
- new_reg = old_reg | GPIO_DX_FRONT_PANEL;
- else
- new_reg = old_reg & ~GPIO_DX_FRONT_PANEL;
- oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
- spin_unlock_irq(&chip->reg_lock);
- return old_reg != new_reg;
-}
-
static const struct snd_kcontrol_new front_panel_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Front Panel Switch",
.info = snd_ctl_boolean_mono_info,
- .get = front_panel_get,
- .put = front_panel_put,
+ .get = gpio_bit_switch_get,
+ .put = gpio_bit_switch_put,
+ .private_value = GPIO_DX_FRONT_PANEL,
};
-static void xonar_dx_ac97_switch(struct oxygen *chip,
- unsigned int reg, unsigned int mute)
+static void xonar_line_mic_ac97_switch(struct oxygen *chip,
+ unsigned int reg, unsigned int mute)
{
if (reg == AC97_LINE) {
spin_lock_irq(&chip->reg_lock);
@@ -508,113 +756,164 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
return 0;
}
-static int xonar_dx_control_filter(struct snd_kcontrol_new *template)
+static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
{
if (!strncmp(template->name, "CD Capture ", 11))
return 1; /* no CD input */
return 0;
}
-static int xonar_mixer_init(struct oxygen *chip)
+static int xonar_d2_mixer_init(struct oxygen *chip)
{
return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
}
-static int xonar_dx_mixer_init(struct oxygen *chip)
+static int xonar_d1_mixer_init(struct oxygen *chip)
{
return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
}
-static const struct oxygen_model xonar_models[] = {
- [MODEL_D2] = {
- .shortname = "Xonar D2",
- .longname = "Asus Virtuoso 200",
- .chip = "AV200",
- .owner = THIS_MODULE,
- .init = xonar_d2_init,
- .control_filter = xonar_d2_control_filter,
- .mixer_init = xonar_mixer_init,
- .cleanup = xonar_cleanup,
- .set_dac_params = set_pcm1796_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .dac_tlv = pcm1796_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2 |
- CAPTURE_1_FROM_SPDIF,
- .dac_channels = 8,
- .dac_volume_min = 0x0f,
- .dac_volume_max = 0xff,
- .misc_flags = OXYGEN_MISC_MIDI,
- .function_flags = OXYGEN_FUNCTION_SPI |
- OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- },
- [MODEL_D2X] = {
- .shortname = "Xonar D2X",
- .longname = "Asus Virtuoso 200",
- .chip = "AV200",
- .owner = THIS_MODULE,
- .init = xonar_d2x_init,
- .control_filter = xonar_d2_control_filter,
- .mixer_init = xonar_mixer_init,
- .cleanup = xonar_cleanup,
- .set_dac_params = set_pcm1796_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .gpio_changed = xonar_gpio_changed,
- .dac_tlv = pcm1796_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2 |
- CAPTURE_1_FROM_SPDIF,
- .dac_channels = 8,
- .dac_volume_min = 0x0f,
- .dac_volume_max = 0xff,
- .misc_flags = OXYGEN_MISC_MIDI,
- .function_flags = OXYGEN_FUNCTION_SPI |
- OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- },
- [MODEL_DX] = {
- .shortname = "Xonar DX",
- .longname = "Asus Virtuoso 100",
- .chip = "AV200",
- .owner = THIS_MODULE,
- .init = xonar_dx_init,
- .control_filter = xonar_dx_control_filter,
- .mixer_init = xonar_dx_mixer_init,
- .cleanup = xonar_dx_cleanup,
- .set_dac_params = set_cs43xx_params,
- .set_adc_params = set_cs53x1_params,
- .update_dac_volume = update_cs43xx_volume,
- .update_dac_mute = update_cs43xx_mute,
- .gpio_changed = xonar_gpio_changed,
- .ac97_switch = xonar_dx_ac97_switch,
- .dac_tlv = cs4362a_db_scale,
- .model_data_size = sizeof(struct xonar_data),
- .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2,
- .dac_channels = 8,
- .dac_volume_min = 0,
- .dac_volume_max = 127,
- .function_flags = OXYGEN_FUNCTION_2WIRE,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- },
+static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data)
+{
+ static const char *const names[] = {
+ [MODEL_D1] = "Xonar D1",
+ [MODEL_DX] = "Xonar DX",
+ [MODEL_D2] = "Xonar D2",
+ [MODEL_D2X] = "Xonar D2X",
+ [MODEL_HDAV] = "Xonar HDAV1.3",
+ [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
+ };
+ static const u8 dacs[] = {
+ [MODEL_D1] = 2,
+ [MODEL_DX] = 2,
+ [MODEL_D2] = 4,
+ [MODEL_D2X] = 4,
+ [MODEL_HDAV] = 1,
+ [MODEL_HDAV_H6] = 4,
+ };
+ struct xonar_data *data = chip->model_data;
+
+ data->model = driver_data;
+ if (data->model == MODEL_HDAV) {
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_HDAV_DB_MASK);
+ switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+ GPIO_HDAV_DB_MASK) {
+ case GPIO_HDAV_DB_H6:
+ data->model = MODEL_HDAV_H6;
+ break;
+ case GPIO_HDAV_DB_XX:
+ snd_printk(KERN_ERR "unknown daughterboard\n");
+ return -ENODEV;
+ }
+ }
+
+ data->dacs = dacs[data->model];
+ chip->model.shortname = names[data->model];
+ return 0;
+}
+
+static const struct oxygen_model model_xonar_d2 = {
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .probe = xonar_model_probe,
+ .init = xonar_d2_init,
+ .control_filter = xonar_d2_control_filter,
+ .mixer_init = xonar_d2_mixer_init,
+ .cleanup = xonar_d2_cleanup,
+ .suspend = xonar_d2_suspend,
+ .resume = xonar_d2_resume,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF |
+ MIDI_OUTPUT |
+ MIDI_INPUT,
+ .dac_channels = 8,
+ .dac_volume_min = 0x0f,
+ .dac_volume_max = 0xff,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_d1 = {
+ .longname = "Asus Virtuoso 100",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .probe = xonar_model_probe,
+ .init = xonar_d1_init,
+ .control_filter = xonar_d1_control_filter,
+ .mixer_init = xonar_d1_mixer_init,
+ .cleanup = xonar_d1_cleanup,
+ .suspend = xonar_d1_suspend,
+ .resume = xonar_d1_resume,
+ .set_dac_params = set_cs43xx_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_cs43xx_volume,
+ .update_dac_mute = update_cs43xx_mute,
+ .ac97_switch = xonar_line_mic_ac97_switch,
+ .dac_tlv = cs4362a_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 8,
+ .dac_volume_min = 0,
+ .dac_volume_max = 127,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_hdav = {
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .probe = xonar_model_probe,
+ .init = xonar_hdav_init,
+ .cleanup = xonar_hdav_cleanup,
+ .suspend = xonar_hdav_suspend,
+ .resume = xonar_hdav_resume,
+ .pcm_hardware_filter = xonar_hdav_pcm_hardware_filter,
+ .set_dac_params = set_hdav_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .uart_input = xonar_hdav_uart_input,
+ .ac97_switch = xonar_line_mic_ac97_switch,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 8,
+ .dac_volume_min = 0x0f,
+ .dac_volume_max = 0xff,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .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 const struct oxygen_model *const models[] = {
+ [MODEL_D1] = &model_xonar_d1,
+ [MODEL_DX] = &model_xonar_d1,
+ [MODEL_D2] = &model_xonar_d2,
+ [MODEL_D2X] = &model_xonar_d2,
+ [MODEL_HDAV] = &model_xonar_hdav,
+ };
static int dev;
int err;
@@ -624,8 +923,10 @@ static int __devinit xonar_probe(struct pci_dev *pci,
++dev;
return -ENOENT;
}
+ BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));
err = oxygen_pci_probe(pci, index[dev], id[dev],
- &xonar_models[pci_id->driver_data]);
+ models[pci_id->driver_data],
+ pci_id->driver_data);
if (err >= 0)
++dev;
return err;
@@ -636,6 +937,10 @@ static struct pci_driver xonar_driver = {
.id_table = xonar_ids,
.probe = xonar_probe,
.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+ .suspend = oxygen_pci_suspend,
+ .resume = oxygen_pci_resume,
+#endif
};
static int __init alsa_card_xonar_init(void)
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 7fdcdc8c6b6..0e06c6c9fcc 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -464,7 +464,8 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
- snd_assert(subs->runtime->dma_bytes < 0x200000); /* max buffer size is 2 MByte */
+ /* max buffer size is 2 MByte */
+ snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
rmh.cmd[1] = subs->runtime->dma_bytes * 8; /* size in bits */
rmh.cmd[2] = subs->runtime->dma_addr >> 24; /* most significant byte */
rmh.cmd[2] |= 1<<19; /* this is a circular buffer */
@@ -516,7 +517,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
int capture_mask = 0;
int playback_mask = 0;
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
struct timeval my_tv1, my_tv2;
do_gettimeofday(&my_tv1);
#endif
@@ -623,7 +624,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
mutex_unlock(&mgr->setup_mutex);
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
do_gettimeofday(&my_tv2);
snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n",
(long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
@@ -1228,7 +1229,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
return -ENOMEM;
}
- snd_assert(pci_id->driver_data < PCI_ID_LAST, return -ENODEV);
+ if (snd_BUG_ON(pci_id->driver_data >= PCI_ID_LAST))
+ return -ENODEV;
card_name = pcxhr_board_params[pci_id->driver_data].board_name;
mgr->playback_chips = pcxhr_board_params[pci_id->driver_data].playback_chips;
mgr->capture_chips = pcxhr_board_params[pci_id->driver_data].capture_chips;
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 78aa81feaa4..7143259cfe3 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -269,7 +269,7 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
unsigned int chipsc;
unsigned char data;
unsigned char mask;
- unsigned char *image;
+ const unsigned char *image;
/* test first xilinx */
chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
@@ -316,19 +316,23 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
int err;
unsigned int i;
unsigned int len;
- unsigned char *data;
+ const unsigned char *data;
unsigned char dummy;
/* check the length of boot image */
- snd_assert(dsp->size > 0, return -EINVAL);
- snd_assert(dsp->size % 3 == 0, return -EINVAL);
- snd_assert(dsp->data, return -EINVAL);
+ if (dsp->size <= 0)
+ return -EINVAL;
+ if (dsp->size % 3)
+ return -EINVAL;
+ if (snd_BUG_ON(!dsp->data))
+ return -EINVAL;
/* transfert data buffer from PC to DSP */
for (i = 0; i < dsp->size; i += 3) {
data = dsp->data + i;
if (i == 0) {
/* test data header consistency */
len = (unsigned int)((data[0]<<16) + (data[1]<<8) + data[2]);
- snd_assert((len==0) || (dsp->size == (len+2)*3), return -EINVAL);
+ if (len && dsp->size != (len + 2) * 3)
+ return -EINVAL;
}
/* wait DSP ready for new transfer */
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
@@ -389,7 +393,8 @@ int pcxhr_load_boot_binary(struct pcxhr_mgr *mgr, const struct firmware *boot)
unsigned char dummy;
/* send the hostport address to the DSP (only the upper 24 bit !) */
- snd_assert((physaddr & 0xff) == 0, return -EINVAL);
+ if (snd_BUG_ON(physaddr & 0xff))
+ return -EINVAL;
PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX1, (physaddr >> 8));
err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_BOOT, 0);
@@ -473,7 +478,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
[CMD_AUDIO_LEVEL_ADJUST] = { 0xc22000, 0, RMH_SSIZE_FIXED },
};
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
static char* cmd_names[] = {
[CMD_VERSION] = "CMD_VERSION",
[CMD_SUPPORTED] = "CMD_SUPPORTED",
@@ -549,7 +554,7 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
}
}
}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
snd_printdd(" stat[%d]=%x\n", i, data);
#endif
@@ -570,7 +575,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
u32 data;
unsigned char reg;
- snd_assert(rmh->cmd_len<PCXHR_SIZE_MAX_CMD, return -EINVAL);
+ if (snd_BUG_ON(rmh->cmd_len >= PCXHR_SIZE_MAX_CMD))
+ return -EINVAL;
err = pcxhr_send_it_dsp(mgr, PCXHR_IT_MESSAGE, 1);
if (err) {
snd_printk(KERN_ERR "pcxhr_send_message : ED_DSP_CRASHED\n");
@@ -597,7 +603,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data |= 0x008000; /* MASK_MORE_THAN_1_WORD_COMMAND */
else
data &= 0xff7fff; /* MASK_1_WORD_COMMAND */
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]);
#endif
@@ -624,7 +630,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
for (i=1; i < rmh->cmd_len; i++) {
/* send other words */
data = rmh->cmd[i];
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
snd_printdd(" cmd[%d]=%x\n", i, data);
#endif
@@ -677,7 +683,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
*/
void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd)
{
- snd_assert(cmd < CMD_LAST_INDEX, return);
+ if (snd_BUG_ON(cmd >= CMD_LAST_INDEX))
+ return;
rmh->cmd[0] = pcxhr_dsp_cmds[cmd].opcode;
rmh->cmd_len = 1;
rmh->stat_len = pcxhr_dsp_cmds[cmd].st_length;
@@ -690,17 +697,17 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,
unsigned int param1, unsigned int param2,
unsigned int param3)
{
- snd_assert(param1 <= MASK_FIRST_FIELD);
+ snd_BUG_ON(param1 > MASK_FIRST_FIELD);
if (capture)
rmh->cmd[0] |= 0x800; /* COMMAND_RECORD_MASK */
if (param1)
rmh->cmd[0] |= (param1 << FIELD_SIZE);
if (param2) {
- snd_assert(param2 <= MASK_FIRST_FIELD);
+ snd_BUG_ON(param2 > MASK_FIRST_FIELD);
rmh->cmd[0] |= param2;
}
if(param3) {
- snd_assert(param3 <= MASK_DSP_WORD);
+ snd_BUG_ON(param3 > MASK_DSP_WORD);
rmh->cmd[1] = param3;
rmh->cmd_len = 2;
}
@@ -847,7 +854,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
int state, i, err;
int audio_mask;
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
struct timeval my_tv1, my_tv2;
do_gettimeofday(&my_tv1);
#endif
@@ -894,7 +901,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
if (err)
return err;
}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
do_gettimeofday(&my_tv2);
snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n",
(long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
@@ -951,7 +958,7 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
enum pcxhr_async_err_src err_src, int pipe,
int is_capture)
{
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
static char* err_src_name[] = {
[PCXHR_ERR_PIPE] = "Pipe",
[PCXHR_ERR_STREAM] = "Stream",
@@ -1169,7 +1176,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
mgr->dsp_time_last, dsp_time_new);
mgr->dsp_time_err++;
}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
if (dsp_time_diff == 0)
snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);
else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))
@@ -1208,7 +1215,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
mgr->src_it_dsp = reg;
tasklet_hi_schedule(&mgr->msg_taskq);
}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
if (reg & PCXHR_FATAL_DSP_ERR)
snd_printdd("FATAL DSP ERROR : %x\n", reg);
#endif
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index e6a4bfbb91b..96640d9c227 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -65,15 +65,18 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
if (err)
return err;
/* test 8 or 12 phys out */
- snd_assert((rmh.stat[0] & MASK_FIRST_FIELD) == mgr->playback_chips*2,
- return -EINVAL);
+ if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2)
+ return -EINVAL;
/* test 8 or 2 phys in */
- snd_assert(((rmh.stat[0] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD) ==
- mgr->capture_chips * 2, return -EINVAL);
+ if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) !=
+ mgr->capture_chips * 2)
+ return -EINVAL;
/* test max nb substream per board */
- snd_assert((rmh.stat[1] & 0x5F) >= card_streams, return -EINVAL);
+ if ((rmh.stat[1] & 0x5F) < card_streams)
+ return -EINVAL;
/* test max nb substream per pipe */
- snd_assert(((rmh.stat[1]>>7)&0x5F) >= PCXHR_PLAYBACK_STREAMS, return -EINVAL);
+ if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS)
+ return -EINVAL;
pcxhr_init_rmh(&rmh, CMD_VERSION);
/* firmware num for DSP */
@@ -394,7 +397,7 @@ static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw,
(unsigned long)fw.size);
return -ENOMEM;
}
- if (copy_from_user(fw.data, dsp->image, dsp->length)) {
+ if (copy_from_user((void *)fw.data, dsp->image, dsp->length)) {
vfree(fw.data);
return -EFAULT;
}
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 979f7da641c..e9f0706ed3e 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -682,7 +682,7 @@ static union firmware_version firmware_versions[] = {
},
};
-static u32 atoh(unsigned char *in, unsigned int len)
+static u32 atoh(const unsigned char *in, unsigned int len)
{
u32 sum = 0;
unsigned int mult = 1;
@@ -702,12 +702,12 @@ static u32 atoh(unsigned char *in, unsigned int len)
return sum;
}
-static int senddata(struct cmdif *cif, unsigned char *in, u32 offset)
+static int senddata(struct cmdif *cif, const unsigned char *in, u32 offset)
{
u32 addr;
u32 data;
u32 i;
- unsigned char *p;
+ const unsigned char *p;
i = atoh(&in[1], 2);
addr = offset + atoh(&in[3], 4);
@@ -726,10 +726,10 @@ static int senddata(struct cmdif *cif, unsigned char *in, u32 offset)
return 0;
}
-static int loadfirmware(struct cmdif *cif, unsigned char *img,
+static int loadfirmware(struct cmdif *cif, const unsigned char *img,
unsigned int size)
{
- unsigned char *in;
+ const unsigned char *in;
u32 laddr, saddr, t, val;
int err = 0;
@@ -865,7 +865,8 @@ static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm,
struct riptideport *hwport;
struct cmdport *cmdport = NULL;
- snd_assert(cif, return -EINVAL);
+ if (snd_BUG_ON(!cif))
+ return -EINVAL;
hwport = cif->hwport;
if (cif->errcnt > MAX_ERROR_COUNT) {
@@ -1482,7 +1483,6 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
{
struct snd_riptide *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
struct pcmhw *data = get_pcmhwdev(substream);
struct cmdif *cif = chip->cif;
unsigned char *lbuspath = NULL;
@@ -1490,7 +1490,8 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
int err = 0;
snd_pcm_format_t format;
- snd_assert(cif && data, return -EINVAL);
+ if (snd_BUG_ON(!cif || !data))
+ return -EINVAL;
snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id,
runtime->channels, runtime->format, runtime->rate);
@@ -1513,9 +1514,9 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
lbuspath = data->paths.stereo;
break;
}
- snd_printdd("use sgdlist at 0x%p and buffer at 0x%p\n",
- data->sgdlist.area, sgbuf);
- if (data->sgdlist.area && sgbuf) {
+ snd_printdd("use sgdlist at 0x%p\n",
+ data->sgdlist.area);
+ if (data->sgdlist.area) {
unsigned int i, j, size, pages, f, pt, period;
struct sgd *c, *p = NULL;
@@ -1533,6 +1534,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
pt = 0;
j = 0;
for (i = 0; i < pages; i++) {
+ unsigned int ofs, addr;
c = &data->sgdbuf[i];
if (p)
p->dwNextLink = cpu_to_le32(data->sgdlist.addr +
@@ -1540,8 +1542,9 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
sizeof(struct
sgd)));
c->dwNextLink = cpu_to_le32(data->sgdlist.addr);
- c->dwSegPtrPhys =
- cpu_to_le32(sgbuf->table[j].addr + pt);
+ ofs = j << PAGE_SHIFT;
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs) + pt;
+ c->dwSegPtrPhys = cpu_to_le32(addr);
pt = (pt + f) % PAGE_SIZE;
if (pt == 0)
j++;
@@ -1772,7 +1775,8 @@ snd_riptide_codec_write(struct snd_ac97 *ac97, unsigned short reg,
union cmdret rptr = CMDRET_ZERO;
int i = 0;
- snd_assert(cif, return);
+ if (snd_BUG_ON(!cif))
+ return;
snd_printdd("Write AC97 reg 0x%x 0x%x\n", reg, val);
do {
@@ -1790,7 +1794,8 @@ static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97,
struct cmdif *cif = chip->cif;
union cmdret rptr = CMDRET_ZERO;
- snd_assert(cif, return 0);
+ if (snd_BUG_ON(!cif))
+ return 0;
if (SEND_RACR(cif, reg, &rptr) != 0)
SEND_RACR(cif, reg, &rptr);
@@ -1804,7 +1809,8 @@ static int snd_riptide_initialize(struct snd_riptide *chip)
unsigned int device_id;
int err;
- snd_assert(chip, return -EINVAL);
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
cif = chip->cif;
if (!cif) {
@@ -1836,7 +1842,8 @@ static int snd_riptide_free(struct snd_riptide *chip)
{
struct cmdif *cif;
- snd_assert(chip, return 0);
+ if (!chip)
+ return 0;
if ((cif = chip->cif)) {
SET_GRESET(cif->hwport);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 4d6fbb36ab8..d723543bead 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -1036,7 +1036,7 @@ static void hdsp_set_dds_value(struct hdsp *hdsp, int rate)
n = DDS_NUMERATOR;
div64_32(&n, rate, &r);
/* n should be less than 2^32 for being written to FREQ register */
- snd_assert((n >> 32) == 0);
+ snd_BUG_ON(n >> 32);
/* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS
value to write it after a reset */
hdsp->dds_value = n;
@@ -3043,7 +3043,7 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
offset = ucontrol->id.index - 1;
- snd_assert(offset >= 0);
+ snd_BUG_ON(offset < 0);
switch (hdsp->io_type) {
case Digiface:
@@ -3767,7 +3767,8 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp,
{
int mapped_channel;
- snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL);
+ if (snd_BUG_ON(channel < 0 || channel >= hdsp->max_channels))
+ return NULL;
if ((mapped_channel = hdsp->channel_map[channel]) < 0)
return NULL;
@@ -3784,10 +3785,12 @@ static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, int chann
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
char *channel_buf;
- snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+ if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4))
+ return -EINVAL;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
if (copy_from_user(channel_buf + pos * 4, src, count * 4))
return -EFAULT;
return count;
@@ -3799,10 +3802,12 @@ static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, int channe
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
char *channel_buf;
- snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+ if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4))
+ return -EINVAL;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
return -EFAULT;
return count;
@@ -3815,7 +3820,8 @@ static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel,
char *channel_buf;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
memset(channel_buf + pos * 4, 0, count * 4);
return count;
}
@@ -3927,7 +3933,8 @@ static int snd_hdsp_channel_info(struct snd_pcm_substream *substream,
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
int mapped_channel;
- snd_assert(info->channel < hdsp->max_channels, return -EINVAL);
+ if (snd_BUG_ON(info->channel >= hdsp->max_channels))
+ return -EINVAL;
if ((mapped_channel = hdsp->channel_map[info->channel]) < 0)
return -EINVAL;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index ab423bc8234..98762f909d6 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -535,7 +535,8 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm);
static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm);
static int hdspm_autosync_ref(struct hdspm * hdspm);
static int snd_hdspm_set_defaults(struct hdspm * hdspm);
-static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf,
+static void hdspm_set_sgbuf(struct hdspm * hdspm,
+ struct snd_pcm_substream *substream,
unsigned int reg, int channels);
static inline int HDSPM_bit2freq(int n)
@@ -845,7 +846,7 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
n = 110100480000000ULL; /* Value checked for AES32 and MADI */
div64_32(&n, rate, &r);
/* n should be less than 2^32 for being written to FREQ register */
- snd_assert((n >> 32) == 0);
+ snd_BUG_ON(n >> 32);
hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
}
@@ -2617,8 +2618,8 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
channel = ucontrol->id.index - 1;
- snd_assert(channel >= 0
- || channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+ if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+ return -EINVAL;
mapped_channel = hdspm->channel_map[channel];
if (mapped_channel < 0)
@@ -2652,8 +2653,8 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
channel = ucontrol->id.index - 1;
- snd_assert(channel >= 0
- || channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+ if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+ return -EINVAL;
mapped_channel = hdspm->channel_map[channel];
if (mapped_channel < 0)
@@ -3496,8 +3497,8 @@ static char *hdspm_channel_buffer_location(struct hdspm * hdspm,
{
int mapped_channel;
- snd_assert(channel >= 0
- || channel < HDSPM_MAX_CHANNELS, return NULL);
+ if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+ return NULL;
mapped_channel = hdspm->channel_map[channel];
if (mapped_channel < 0)
@@ -3520,14 +3521,15 @@ static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream,
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
char *channel_buf;
- snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4,
- return -EINVAL);
+ if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
+ return -EINVAL;
channel_buf =
hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
return copy_from_user(channel_buf + pos * 4, src, count * 4);
}
@@ -3539,13 +3541,14 @@ static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream,
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
char *channel_buf;
- snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4,
- return -EINVAL);
+ if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
+ return -EINVAL;
channel_buf =
hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
return copy_to_user(dst, channel_buf + pos * 4, count * 4);
}
@@ -3559,7 +3562,8 @@ static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream,
channel_buf =
hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
memset(channel_buf + pos * 4, 0, count * 4);
return 0;
}
@@ -3601,8 +3605,6 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
int i;
pid_t this_pid;
pid_t other_pid;
- struct snd_sg_buf *sgbuf;
-
spin_lock_irq(&hdspm->lock);
@@ -3670,11 +3672,9 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- sgbuf = snd_pcm_substream_sgbuf(substream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferOut,
+ hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,
params_channels(params));
for (i = 0; i < params_channels(params); ++i)
@@ -3685,7 +3685,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
snd_printdd("Allocated sample buffer for playback at %p\n",
hdspm->playback_buffer);
} else {
- hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn,
+ hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,
params_channels(params));
for (i = 0; i < params_channels(params); ++i)
@@ -3700,7 +3700,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
"playback" : "capture",
- snd_pcm_sgbuf_get_addr(sgbuf, 0));
+ snd_pcm_sgbuf_get_addr(substream, 0));
*/
/*
snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
@@ -3744,7 +3744,8 @@ static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
int mapped_channel;
- snd_assert(info->channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+ if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS))
+ return -EINVAL;
mapped_channel = hdspm->channel_map[info->channel];
if (mapped_channel < 0)
@@ -4249,13 +4250,14 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
return 0;
}
-static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf,
+static void hdspm_set_sgbuf(struct hdspm * hdspm,
+ struct snd_pcm_substream *substream,
unsigned int reg, int channels)
{
int i;
for (i = 0; i < (channels * 16); i++)
hdspm_write(hdspm, reg + 4 * i,
- snd_pcm_sgbuf_get_addr(sgbuf, (size_t) 4096 * i));
+ snd_pcm_sgbuf_get_addr(substream, 4096 * i));
}
/* ------------- ALSA Devices ---------------------------- */
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index a123f0e6ba2..2570907134d 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -595,8 +595,6 @@ static void rme9652_set_thru(struct snd_rme9652 *rme9652, int channel, int enabl
} else {
int mapped_channel;
- snd_assert(channel == RME9652_NCHANNELS, return);
-
mapped_channel = rme9652->channel_map[channel];
if (enable) {
@@ -1893,7 +1891,8 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
{
int mapped_channel;
- snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL);
+ if (snd_BUG_ON(channel < 0 || channel >= RME9652_NCHANNELS))
+ return NULL;
if ((mapped_channel = rme9652->channel_map[channel]) < 0) {
return NULL;
@@ -1914,12 +1913,14 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int ch
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
char *channel_buf;
- snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+ if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4))
+ return -EINVAL;
channel_buf = rme9652_channel_buffer_location (rme9652,
substream->pstr->stream,
channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
if (copy_from_user(channel_buf + pos * 4, src, count * 4))
return -EFAULT;
return count;
@@ -1931,12 +1932,14 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int cha
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
char *channel_buf;
- snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+ if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4))
+ return -EINVAL;
channel_buf = rme9652_channel_buffer_location (rme9652,
substream->pstr->stream,
channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
return -EFAULT;
return count;
@@ -1951,7 +1954,8 @@ static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int chann
channel_buf = rme9652_channel_buffer_location (rme9652,
substream->pstr->stream,
channel);
- snd_assert(channel_buf != NULL, return -EIO);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
memset(channel_buf + pos * 4, 0, count * 4);
return count;
}
@@ -2053,7 +2057,8 @@ static int snd_rme9652_channel_info(struct snd_pcm_substream *substream,
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
int chn;
- snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL);
+ if (snd_BUG_ON(info->channel >= RME9652_NCHANNELS))
+ return -EINVAL;
if ((chn = rme9652->channel_map[info->channel]) < 0) {
return -EINVAL;
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 0d3d305b0a0..cd408b86c83 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -534,8 +534,8 @@ static int snd_sonicvibes_hw_constraint_dac_rate(struct snd_pcm_hw_params *param
params->rate_den = 1;
} else {
snd_sonicvibes_pll(rate, &r, &m, &n);
- snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL);
- snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL);
+ snd_BUG_ON(SV_REFFREQUENCY % 16);
+ snd_BUG_ON(SV_ADCMULT % 512);
params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r;
params->rate_den = (SV_ADCMULT/512) * (m+2);
}
@@ -849,7 +849,8 @@ static int __devinit snd_sonicvibes_pcm(struct sonicvibes * sonic, int device, s
if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)
return err;
- snd_assert(pcm != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pcm))
+ return -EINVAL;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops);
@@ -1089,7 +1090,8 @@ static int __devinit snd_sonicvibes_mixer(struct sonicvibes * sonic)
unsigned int idx;
int err;
- snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!sonic || !sonic->card))
+ return -EINVAL;
card = sonic->card;
strcpy(card->mixername, "S3 SonicVibes");
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index bbcee2c09ae..c612b435ca2 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -1590,7 +1590,10 @@ static int snd_trident_trigger(struct snd_pcm_substream *substream,
if (spdif_flag) {
if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
- outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+ val = trident->spdif_pcm_ctrl;
+ if (!go)
+ val &= ~(0x28);
+ outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
} else {
outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
@@ -2928,7 +2931,8 @@ static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
{
struct snd_trident_pcm_mixer *tmix;
- snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL);
+ if (snd_BUG_ON(!trident || !voice || !substream))
+ return -EINVAL;
tmix = &trident->pcm_mixer[substream->number];
tmix->voice = voice;
tmix->vol = T4D_DEFAULT_PCM_VOL;
@@ -2943,7 +2947,8 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr
{
struct snd_trident_pcm_mixer *tmix;
- snd_assert(trident != NULL && substream != NULL, return -EINVAL);
+ if (snd_BUG_ON(!trident || !substream))
+ return -EINVAL;
tmix = &trident->pcm_mixer[substream->number];
tmix->voice = NULL;
snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
@@ -3128,7 +3133,8 @@ static unsigned char snd_trident_gameport_read(struct gameport *gameport)
{
struct snd_trident *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
return inb(TRID_REG(chip, GAMEPORT_LEGACY));
}
@@ -3136,7 +3142,8 @@ static void snd_trident_gameport_trigger(struct gameport *gameport)
{
struct snd_trident *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return);
+ if (snd_BUG_ON(!chip))
+ return;
outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
}
@@ -3145,7 +3152,8 @@ static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes
struct snd_trident *chip = gameport_get_port_data(gameport);
int i;
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
*buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
@@ -3161,7 +3169,8 @@ static int snd_trident_gameport_open(struct gameport *gameport, int mode)
{
struct snd_trident *chip = gameport_get_port_data(gameport);
- snd_assert(chip, return 0);
+ if (snd_BUG_ON(!chip))
+ return 0;
switch (mode) {
case GAMEPORT_MODE_COOKED:
@@ -3888,8 +3897,8 @@ static void snd_trident_clear_voices(struct snd_trident * trident, unsigned shor
{
unsigned int i, val, mask[2] = { 0, 0 };
- snd_assert(v_min <= 63, return);
- snd_assert(v_max <= 63, return);
+ if (snd_BUG_ON(v_min > 63 || v_max > 63))
+ return;
for (i = v_min; i <= v_max; i++)
mask[i >> 5] |= 1 << (i & 0x1f);
if (mask[0]) {
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index df9b487fa17..f9779e23fe5 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -194,11 +194,14 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident,
struct snd_util_memblk *blk;
struct snd_pcm_runtime *runtime = substream->runtime;
int idx, page;
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
- snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+ if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+ runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
+ SNDRV_TRIDENT_PAGE_SIZE))
+ return NULL;
hdr = trident->tlb.memhdr;
- snd_assert(hdr != NULL, return NULL);
+ if (snd_BUG_ON(!hdr))
+ return NULL;
@@ -208,18 +211,14 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident,
mutex_unlock(&hdr->block_mutex);
return NULL;
}
- if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) {
- snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk));
- __snd_util_mem_free(hdr, blk);
- mutex_unlock(&hdr->block_mutex);
- return NULL;
- }
/* set TLB entries */
idx = 0;
for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) {
- dma_addr_t addr = sgbuf->table[idx].addr;
- unsigned long ptr = (unsigned long)sgbuf->table[idx].buf;
+ unsigned long ofs = idx << PAGE_SHIFT;
+ dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ unsigned long ptr = (unsigned long)
+ snd_pcm_sgbuf_get_ptr(substream, ofs);
if (! is_valid_page(addr)) {
__snd_util_mem_free(hdr, blk);
mutex_unlock(&hdr->block_mutex);
@@ -245,9 +244,13 @@ snd_trident_alloc_cont_pages(struct snd_trident *trident,
dma_addr_t addr;
unsigned long ptr;
- snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+ if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+ runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
+ SNDRV_TRIDENT_PAGE_SIZE))
+ return NULL;
hdr = trident->tlb.memhdr;
- snd_assert(hdr != NULL, return NULL);
+ if (snd_BUG_ON(!hdr))
+ return NULL;
mutex_lock(&hdr->block_mutex);
blk = search_empty(hdr, runtime->dma_bytes);
@@ -279,8 +282,8 @@ struct snd_util_memblk *
snd_trident_alloc_pages(struct snd_trident *trident,
struct snd_pcm_substream *substream)
{
- snd_assert(trident != NULL, return NULL);
- snd_assert(substream != NULL, return NULL);
+ if (snd_BUG_ON(!trident || !substream))
+ return NULL;
if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG)
return snd_trident_alloc_sg_pages(trident, substream);
else
@@ -297,8 +300,8 @@ int snd_trident_free_pages(struct snd_trident *trident,
struct snd_util_memhdr *hdr;
int page;
- snd_assert(trident != NULL, return -EINVAL);
- snd_assert(blk != NULL, return -EINVAL);
+ if (snd_BUG_ON(!trident || !blk))
+ return -EINVAL;
hdr = trident->tlb.memhdr;
mutex_lock(&hdr->block_mutex);
@@ -310,181 +313,3 @@ int snd_trident_free_pages(struct snd_trident *trident,
mutex_unlock(&hdr->block_mutex);
return 0;
}
-
-
-/*----------------------------------------------------------------
- * memory allocation using multiple pages (for synth)
- *----------------------------------------------------------------
- * Unlike the DMA allocation above, non-contiguous pages are
- * assigned to TLB.
- *----------------------------------------------------------------*/
-
-/*
- */
-static int synth_alloc_pages(struct snd_trident *hw, struct snd_util_memblk *blk);
-static int synth_free_pages(struct snd_trident *hw, struct snd_util_memblk *blk);
-
-/*
- * allocate a synth sample area
- */
-struct snd_util_memblk *
-snd_trident_synth_alloc(struct snd_trident *hw, unsigned int size)
-{
- struct snd_util_memblk *blk;
- struct snd_util_memhdr *hdr = hw->tlb.memhdr;
-
- mutex_lock(&hdr->block_mutex);
- blk = __snd_util_mem_alloc(hdr, size);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
- return NULL;
- }
- if (synth_alloc_pages(hw, blk)) {
- __snd_util_mem_free(hdr, blk);
- mutex_unlock(&hdr->block_mutex);
- return NULL;
- }
- mutex_unlock(&hdr->block_mutex);
- return blk;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_alloc);
-
-/*
- * free a synth sample area
- */
-int
-snd_trident_synth_free(struct snd_trident *hw, struct snd_util_memblk *blk)
-{
- struct snd_util_memhdr *hdr = hw->tlb.memhdr;
-
- mutex_lock(&hdr->block_mutex);
- synth_free_pages(hw, blk);
- __snd_util_mem_free(hdr, blk);
- mutex_unlock(&hdr->block_mutex);
- return 0;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_free);
-
-/*
- * reset TLB entry and free kernel page
- */
-static void clear_tlb(struct snd_trident *trident, int page)
-{
- void *ptr = page_to_ptr(trident, page);
- dma_addr_t addr = page_to_addr(trident, page);
- set_silent_tlb(trident, page);
- if (ptr) {
- struct snd_dma_buffer dmab;
- dmab.dev.type = SNDRV_DMA_TYPE_DEV;
- dmab.dev.dev = snd_dma_pci_data(trident->pci);
- dmab.area = ptr;
- dmab.addr = addr;
- dmab.bytes = ALIGN_PAGE_SIZE;
- snd_dma_free_pages(&dmab);
- }
-}
-
-/* check new allocation range */
-static void get_single_page_range(struct snd_util_memhdr *hdr,
- struct snd_util_memblk *blk,
- int *first_page_ret, int *last_page_ret)
-{
- struct list_head *p;
- struct snd_util_memblk *q;
- int first_page, last_page;
- first_page = firstpg(blk);
- if ((p = blk->list.prev) != &hdr->block) {
- q = list_entry(p, struct snd_util_memblk, list);
- if (lastpg(q) == first_page)
- first_page++; /* first page was already allocated */
- }
- last_page = lastpg(blk);
- if ((p = blk->list.next) != &hdr->block) {
- q = list_entry(p, struct snd_util_memblk, list);
- if (firstpg(q) == last_page)
- last_page--; /* last page was already allocated */
- }
- *first_page_ret = first_page;
- *last_page_ret = last_page;
-}
-
-/*
- * allocate kernel pages and assign them to TLB
- */
-static int synth_alloc_pages(struct snd_trident *hw, struct snd_util_memblk *blk)
-{
- int page, first_page, last_page;
- struct snd_dma_buffer dmab;
-
- firstpg(blk) = get_aligned_page(blk->offset);
- lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1);
- get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page);
-
- /* allocate a kernel page for each Trident page -
- * fortunately Trident page size and kernel PAGE_SIZE is identical!
- */
- for (page = first_page; page <= last_page; page++) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(hw->pci),
- ALIGN_PAGE_SIZE, &dmab) < 0)
- goto __fail;
- if (! is_valid_page(dmab.addr)) {
- snd_dma_free_pages(&dmab);
- goto __fail;
- }
- set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr);
- }
- return 0;
-
-__fail:
- /* release allocated pages */
- last_page = page - 1;
- for (page = first_page; page <= last_page; page++)
- clear_tlb(hw, page);
-
- return -ENOMEM;
-}
-
-/*
- * free pages
- */
-static int synth_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk)
-{
- int page, first_page, last_page;
-
- get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page);
- for (page = first_page; page <= last_page; page++)
- clear_tlb(trident, page);
-
- return 0;
-}
-
-/*
- * copy_from_user(blk + offset, data, size)
- */
-int snd_trident_synth_copy_from_user(struct snd_trident *trident,
- struct snd_util_memblk *blk,
- int offset, const char __user *data, int size)
-{
- int page, nextofs, end_offset, temp, temp1;
-
- offset += blk->offset;
- end_offset = offset + size;
- page = get_aligned_page(offset) + 1;
- do {
- nextofs = aligned_page_offset(page);
- temp = nextofs - offset;
- temp1 = end_offset - offset;
- if (temp1 < temp)
- temp = temp1;
- if (copy_from_user(offset_ptr(trident, offset), data, temp))
- return -EFAULT;
- offset = nextofs;
- data += temp;
- page++;
- } while (offset < end_offset);
- return 0;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index b585cc3e4c4..1aafe956ee2 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -313,6 +313,7 @@ struct snd_via_sg_table {
} ;
#define VIA_TABLE_SIZE 255
+#define VIA_MAX_BUFSIZE (1<<24)
struct viadev {
unsigned int reg_offset;
@@ -420,7 +421,6 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
{
unsigned int i, idx, ofs, rest;
struct via82xx *chip = snd_pcm_substream_chip(substream);
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
if (dev->table.area == NULL) {
/* the start of each lists must be aligned to 8 bytes,
@@ -449,15 +449,15 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
do {
unsigned int r;
unsigned int flag;
+ unsigned int addr;
if (idx >= VIA_TABLE_SIZE) {
snd_printk(KERN_ERR "via82xx: too much table size!\n");
return -EINVAL;
}
- ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
- r = PAGE_SIZE - (ofs % PAGE_SIZE);
- if (rest < r)
- r = rest;
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
+ r = snd_pcm_sgbuf_get_chunk_size(substream, ofs, rest);
rest -= r;
if (! rest) {
if (i == periods - 1)
@@ -824,7 +824,8 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
struct viadev *viadev = substream->runtime->private_data;
unsigned int idx, ptr, count, res;
- snd_assert(viadev->tbl_entries, return 0);
+ if (snd_BUG_ON(!viadev->tbl_entries))
+ return 0;
if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
return 0;
@@ -855,7 +856,8 @@ static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *subst
unsigned int idx, count, res;
int status;
- snd_assert(viadev->tbl_entries, return 0);
+ if (snd_BUG_ON(!viadev->tbl_entries))
+ return 0;
spin_lock(&chip->reg_lock);
count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));
@@ -1037,7 +1039,7 @@ static int snd_via8233_playback_prepare(struct snd_pcm_substream *substream)
else
rbits = (0x100000 / 48000) * runtime->rate +
((0x100000 % 48000) * runtime->rate) / 48000;
- snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
+ snd_BUG_ON(rbits & ~0xfffff);
snd_via82xx_channel_reset(chip, viadev);
snd_via82xx_set_table_ptr(chip, viadev);
outb(chip->playback_volume[viadev->reg_offset / 0x10][0],
@@ -1144,9 +1146,9 @@ static struct snd_pcm_hardware snd_via82xx_hw =
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
- .buffer_bytes_max = 128 * 1024,
+ .buffer_bytes_max = VIA_MAX_BUFSIZE,
.period_bytes_min = 32,
- .period_bytes_max = 128 * 1024,
+ .period_bytes_max = VIA_MAX_BUFSIZE / 2,
.periods_min = 2,
.periods_max = VIA_TABLE_SIZE / 2,
.fifo_size = 0,
@@ -1398,10 +1400,9 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
/* capture */
init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
- if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024)) < 0)
- return err;
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, VIA_MAX_BUFSIZE);
/* PCM #1: multi-channel playback and 2nd capture */
err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
@@ -1417,11 +1418,9 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
/* set up capture */
init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 7, 1);
- if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024)) < 0)
- return err;
-
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, VIA_MAX_BUFSIZE);
return 0;
}
@@ -1453,10 +1452,9 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
/* capture */
init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
- if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024)) < 0)
- return err;
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, VIA_MAX_BUFSIZE);
/* SPDIF supported? */
if (! ac97_can_spdif(chip->ac97))
@@ -1473,11 +1471,9 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
/* set up playback */
init_viadev(chip, chip->playback_devno, 0x30, 3, 0);
- if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024)) < 0)
- return err;
-
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, VIA_MAX_BUFSIZE);
return 0;
}
@@ -1505,11 +1501,9 @@ static int __devinit snd_via686_pcm_new(struct via82xx *chip)
init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0);
init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1);
- if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024)) < 0)
- return err;
-
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, VIA_MAX_BUFSIZE);
return 0;
}
@@ -1757,6 +1751,12 @@ static struct ac97_quirk ac97_quirks[] = {
.type = AC97_TUNE_HP_ONLY
},
{
+ .subvendor = 0x1019,
+ .subdevice = 0x1841,
+ .name = "ECS K7VTA3",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
.subvendor = 0x1849,
.subdevice = 0x3059,
.name = "ASRock K7VM2",
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 31f64ee3988..5bd79d2a5a1 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -281,7 +281,6 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
{
unsigned int i, idx, ofs, rest;
struct via82xx_modem *chip = snd_pcm_substream_chip(substream);
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
if (dev->table.area == NULL) {
/* the start of each lists must be aligned to 8 bytes,
@@ -310,12 +309,14 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
do {
unsigned int r;
unsigned int flag;
+ unsigned int addr;
if (idx >= VIA_TABLE_SIZE) {
snd_printk(KERN_ERR "via82xx: too much table size!\n");
return -EINVAL;
}
- ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
r = PAGE_SIZE - (ofs % PAGE_SIZE);
if (rest < r)
r = rest;
@@ -612,7 +613,8 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
struct viadev *viadev = substream->runtime->private_data;
unsigned int idx, ptr, count, res;
- snd_assert(viadev->tbl_entries, return 0);
+ if (snd_BUG_ON(!viadev->tbl_entries))
+ return 0;
if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
return 0;
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index b4bfc1acde8..7e87f398ff0 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -253,7 +253,8 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
int offset = pipe->hw_ptr;
u32 *addr = (u32 *)(runtime->dma_area + offset);
- snd_assert(count % 4 == 0, return);
+ if (snd_BUG_ON(count % 4))
+ return;
vx2_setup_pseudo_dma(chip, 1);
@@ -291,7 +292,8 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
u32 *addr = (u32 *)(runtime->dma_area + offset);
unsigned long port = vx2_reg_addr(chip, VX_DMA);
- snd_assert(count % 4 == 0, return);
+ if (snd_BUG_ON(count % 4))
+ return;
vx2_setup_pseudo_dma(chip, 0);
/* Transfer using pseudo-dma.
@@ -359,7 +361,7 @@ static int vx2_load_xilinx_binary(struct vx_core *chip, const struct firmware *x
{
unsigned int i;
unsigned int port;
- unsigned char *image;
+ const unsigned char *image;
/* XILINX reset (wait at least 1 milisecond between reset on and off). */
vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK);
@@ -675,7 +677,8 @@ static void vx2_write_akm(struct vx_core *chip, int reg, unsigned int data)
a look up table, as there is no linear matching between the driver codec values
and the real dBu value
*/
- snd_assert(data < sizeof(vx2_akm_gains_lut), return);
+ if (snd_BUG_ON(data >= sizeof(vx2_akm_gains_lut)))
+ return;
switch (reg) {
case XX_CODEC_LEVEL_LEFT_REGISTER:
@@ -823,7 +826,8 @@ static void vx2_set_input_level(struct snd_vx222 *chip)
preamp++; /* raise pre ampli + 18dB */
miclevel -= (18 * 2); /* lower level 18 dB (*2 because of 0.5 dB steps !) */
}
- snd_assert(preamp < 4, return);
+ if (snd_BUG_ON(preamp >= 4))
+ return;
/* set pre-amp level */
chip->regSELMIC &= ~MICRO_SELECT_PREAMPLI_MASK;
diff --git a/sound/pci/ymfpci/ymfpci_image.h b/sound/pci/ymfpci/ymfpci_image.h
deleted file mode 100644
index 112f2fff6c8..00000000000
--- a/sound/pci/ymfpci/ymfpci_image.h
+++ /dev/null
@@ -1,1565 +0,0 @@
-#ifndef _HWMCODE_
-#define _HWMCODE_
-
-static u32 DspInst[YDSXG_DSPLENGTH / 4] = {
- 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
- 0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
- 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
- 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000
-};
-
-static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = {
- 0x000007, 0x240007, 0x0C0007, 0x1C0007,
- 0x060007, 0x700002, 0x000020, 0x030040,
- 0x007104, 0x004286, 0x030040, 0x000F0D,
- 0x000810, 0x20043A, 0x000282, 0x00020D,
- 0x000810, 0x20043A, 0x001282, 0x200E82,
- 0x001A82, 0x032D0D, 0x000810, 0x10043A,
- 0x02D38D, 0x000810, 0x18043A, 0x00010D,
- 0x020015, 0x0000FD, 0x000020, 0x038860,
- 0x039060, 0x038060, 0x038040, 0x038040,
- 0x038040, 0x018040, 0x000A7D, 0x038040,
- 0x038040, 0x018040, 0x200402, 0x000882,
- 0x08001A, 0x000904, 0x015986, 0x000007,
- 0x260007, 0x000007, 0x000007, 0x018A06,
- 0x000007, 0x030C8D, 0x000810, 0x18043A,
- 0x260007, 0x00087D, 0x018042, 0x00160A,
- 0x04A206, 0x000007, 0x00218D, 0x000810,
- 0x08043A, 0x21C206, 0x000007, 0x0007FD,
- 0x018042, 0x08000A, 0x000904, 0x029386,
- 0x000195, 0x090D04, 0x000007, 0x000820,
- 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
- 0x032206, 0x018040, 0x000A7D, 0x038042,
- 0x13804A, 0x18000A, 0x001820, 0x059060,
- 0x058860, 0x018040, 0x0000FD, 0x018042,
- 0x70000A, 0x000115, 0x071144, 0x032386,
- 0x030000, 0x007020, 0x034A06, 0x018040,
- 0x00348D, 0x000810, 0x08043A, 0x21EA06,
- 0x000007, 0x02D38D, 0x000810, 0x18043A,
- 0x018206, 0x000007, 0x240007, 0x000F8D,
- 0x000810, 0x00163A, 0x002402, 0x005C02,
- 0x0028FD, 0x000020, 0x018040, 0x08000D,
- 0x000815, 0x510984, 0x000007, 0x00004D,
- 0x000E5D, 0x000E02, 0x00418D, 0x000810,
- 0x08043A, 0x2C8A06, 0x000007, 0x00008D,
- 0x000924, 0x000F02, 0x00458D, 0x000810,
- 0x08043A, 0x2C8A06, 0x000007, 0x00387D,
- 0x018042, 0x08000A, 0x001015, 0x010984,
- 0x018386, 0x000007, 0x01AA06, 0x000007,
- 0x0008FD, 0x018042, 0x18000A, 0x001904,
- 0x218086, 0x280007, 0x001810, 0x28043A,
- 0x280C02, 0x00000D, 0x000810, 0x28143A,
- 0x08808D, 0x000820, 0x0002FD, 0x018040,
- 0x200007, 0x00020D, 0x189904, 0x000007,
- 0x00402D, 0x0000BD, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x055A86, 0x000007,
- 0x000100, 0x000A20, 0x00047D, 0x018040,
- 0x018042, 0x20000A, 0x003015, 0x012144,
- 0x034986, 0x000007, 0x002104, 0x034986,
- 0x000007, 0x000F8D, 0x000810, 0x280C3A,
- 0x023944, 0x06C986, 0x000007, 0x001810,
- 0x28043A, 0x08810D, 0x000820, 0x0002FD,
- 0x018040, 0x200007, 0x002810, 0x78003A,
- 0x00688D, 0x000810, 0x08043A, 0x288A06,
- 0x000007, 0x00400D, 0x001015, 0x189904,
- 0x292904, 0x393904, 0x000007, 0x060206,
- 0x000007, 0x0004F5, 0x00007D, 0x000020,
- 0x00008D, 0x010860, 0x018040, 0x00047D,
- 0x038042, 0x21804A, 0x18000A, 0x021944,
- 0x215886, 0x000007, 0x004075, 0x71F104,
- 0x000007, 0x010042, 0x28000A, 0x002904,
- 0x212086, 0x000007, 0x003C0D, 0x30A904,
- 0x000007, 0x00077D, 0x018042, 0x08000A,
- 0x000904, 0x07DA86, 0x00057D, 0x002820,
- 0x03B060, 0x07F206, 0x018040, 0x003020,
- 0x03A860, 0x018040, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x07FA86, 0x000007,
- 0x00057D, 0x018042, 0x28040A, 0x000E8D,
- 0x000810, 0x280C3A, 0x00000D, 0x000810,
- 0x28143A, 0x09000D, 0x000820, 0x0002FD,
- 0x018040, 0x200007, 0x003DFD, 0x000020,
- 0x018040, 0x00107D, 0x008D8D, 0x000810,
- 0x08043A, 0x288A06, 0x000007, 0x000815,
- 0x08001A, 0x010984, 0x095186, 0x00137D,
- 0x200500, 0x280F20, 0x338F60, 0x3B8F60,
- 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
- 0x038A60, 0x018040, 0x007FBD, 0x383DC4,
- 0x000007, 0x001A7D, 0x001375, 0x018042,
- 0x09004A, 0x10000A, 0x0B8D04, 0x139504,
- 0x000007, 0x000820, 0x019060, 0x001104,
- 0x212086, 0x010040, 0x0017FD, 0x018042,
- 0x08000A, 0x000904, 0x212286, 0x000007,
- 0x00197D, 0x038042, 0x09804A, 0x10000A,
- 0x000924, 0x001664, 0x0011FD, 0x038042,
- 0x2B804A, 0x19804A, 0x00008D, 0x218944,
- 0x000007, 0x002244, 0x0AE186, 0x000007,
- 0x001A64, 0x002A24, 0x00197D, 0x080102,
- 0x100122, 0x000820, 0x039060, 0x018040,
- 0x003DFD, 0x00008D, 0x000820, 0x018040,
- 0x001375, 0x001A7D, 0x010042, 0x09804A,
- 0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
- 0x309144, 0x000007, 0x00060D, 0x000A15,
- 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
- 0x000464, 0x01B3E4, 0x0232E4, 0x000464,
- 0x000464, 0x000464, 0x000464, 0x00040D,
- 0x08B1C4, 0x000007, 0x000820, 0x000BF5,
- 0x030040, 0x00197D, 0x038042, 0x09804A,
- 0x000A24, 0x08000A, 0x080E64, 0x000007,
- 0x100122, 0x000820, 0x031060, 0x010040,
- 0x0064AC, 0x00027D, 0x000020, 0x018040,
- 0x00107D, 0x018042, 0x0011FD, 0x3B804A,
- 0x09804A, 0x20000A, 0x000095, 0x1A1144,
- 0x00A144, 0x0D2086, 0x00040D, 0x00B984,
- 0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
- 0x09804A, 0x28000A, 0x000095, 0x010924,
- 0x002A64, 0x0D1186, 0x000007, 0x002904,
- 0x0D2286, 0x000007, 0x0D2A06, 0x080002,
- 0x00008D, 0x00387D, 0x000820, 0x018040,
- 0x00127D, 0x018042, 0x10000A, 0x003904,
- 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
- 0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
- 0x000015, 0x00082D, 0x02C78D, 0x000820,
- 0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
- 0x0E7186, 0x400025, 0x00008D, 0x110944,
- 0x000007, 0x00018D, 0x109504, 0x000007,
- 0x009164, 0x000424, 0x000424, 0x000424,
- 0x100102, 0x280002, 0x02C68D, 0x000820,
- 0x0EC206, 0x00018D, 0x00042D, 0x00008D,
- 0x109504, 0x000007, 0x00020D, 0x109184,
- 0x000007, 0x02C70D, 0x000820, 0x00008D,
- 0x0038FD, 0x018040, 0x003BFD, 0x001020,
- 0x03A860, 0x000815, 0x313184, 0x212184,
- 0x000007, 0x03B060, 0x03A060, 0x018040,
- 0x0022FD, 0x000095, 0x010924, 0x000424,
- 0x000424, 0x001264, 0x100102, 0x000820,
- 0x039060, 0x018040, 0x001924, 0x00FB8D,
- 0x00397D, 0x000820, 0x058040, 0x038042,
- 0x09844A, 0x000606, 0x08040A, 0x000424,
- 0x000424, 0x00117D, 0x018042, 0x08000A,
- 0x000A24, 0x280502, 0x280C02, 0x09800D,
- 0x000820, 0x0002FD, 0x018040, 0x200007,
- 0x0022FD, 0x018042, 0x08000A, 0x000095,
- 0x280DC4, 0x011924, 0x00197D, 0x018042,
- 0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
- 0x113144, 0x0A8D04, 0x000007, 0x080A44,
- 0x129504, 0x000007, 0x0023FD, 0x001020,
- 0x038040, 0x101244, 0x000007, 0x000820,
- 0x039060, 0x018040, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x10FA86, 0x000007,
- 0x003BFD, 0x000100, 0x000A10, 0x0B807A,
- 0x13804A, 0x090984, 0x000007, 0x000095,
- 0x013D04, 0x118086, 0x10000A, 0x100002,
- 0x090984, 0x000007, 0x038042, 0x11804A,
- 0x090D04, 0x000007, 0x10000A, 0x090D84,
- 0x000007, 0x00257D, 0x000820, 0x018040,
- 0x00010D, 0x000810, 0x28143A, 0x00127D,
- 0x018042, 0x20000A, 0x00197D, 0x018042,
- 0x00117D, 0x31804A, 0x10000A, 0x003124,
- 0x01280D, 0x00397D, 0x000820, 0x058040,
- 0x038042, 0x09844A, 0x000606, 0x08040A,
- 0x300102, 0x003124, 0x000424, 0x000424,
- 0x001224, 0x280502, 0x001A4C, 0x130186,
- 0x700002, 0x00002D, 0x030000, 0x00387D,
- 0x018042, 0x10000A, 0x132A06, 0x002124,
- 0x0000AD, 0x100002, 0x00010D, 0x000924,
- 0x006B24, 0x01368D, 0x00397D, 0x000820,
- 0x058040, 0x038042, 0x09844A, 0x000606,
- 0x08040A, 0x003264, 0x00008D, 0x000A24,
- 0x001020, 0x00227D, 0x018040, 0x013C0D,
- 0x000810, 0x08043A, 0x29D206, 0x000007,
- 0x002820, 0x00207D, 0x018040, 0x00117D,
- 0x038042, 0x13804A, 0x33800A, 0x00387D,
- 0x018042, 0x08000A, 0x000904, 0x163A86,
- 0x000007, 0x00008D, 0x030964, 0x01478D,
- 0x00397D, 0x000820, 0x058040, 0x038042,
- 0x09844A, 0x000606, 0x08040A, 0x380102,
- 0x000424, 0x000424, 0x001224, 0x0002FD,
- 0x018042, 0x08000A, 0x000904, 0x14A286,
- 0x000007, 0x280502, 0x001A4C, 0x163986,
- 0x000007, 0x032164, 0x00632C, 0x003DFD,
- 0x018042, 0x08000A, 0x000095, 0x090904,
- 0x000007, 0x000820, 0x001A4C, 0x156186,
- 0x018040, 0x030000, 0x157A06, 0x002124,
- 0x00010D, 0x000924, 0x006B24, 0x015B8D,
- 0x00397D, 0x000820, 0x058040, 0x038042,
- 0x09844A, 0x000606, 0x08040A, 0x003A64,
- 0x000095, 0x001224, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x15DA86, 0x000007,
- 0x01628D, 0x000810, 0x08043A, 0x29D206,
- 0x000007, 0x14D206, 0x000007, 0x007020,
- 0x08010A, 0x10012A, 0x0020FD, 0x038860,
- 0x039060, 0x018040, 0x00227D, 0x018042,
- 0x003DFD, 0x08000A, 0x31844A, 0x000904,
- 0x16D886, 0x18008B, 0x00008D, 0x189904,
- 0x00312C, 0x17AA06, 0x000007, 0x00324C,
- 0x173386, 0x000007, 0x001904, 0x173086,
- 0x000007, 0x000095, 0x199144, 0x00222C,
- 0x003124, 0x00636C, 0x000E3D, 0x001375,
- 0x000BFD, 0x010042, 0x09804A, 0x10000A,
- 0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
- 0x000007, 0x00008D, 0x189904, 0x00226C,
- 0x00322C, 0x30050A, 0x301DAB, 0x002083,
- 0x0018FD, 0x018042, 0x08000A, 0x018924,
- 0x300502, 0x001083, 0x001875, 0x010042,
- 0x10000A, 0x00008D, 0x010924, 0x001375,
- 0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
- 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
- 0x305C8B, 0x006083, 0x0002F5, 0x010042,
- 0x08000A, 0x000904, 0x187A86, 0x000007,
- 0x001E2D, 0x0005FD, 0x018042, 0x08000A,
- 0x028924, 0x280502, 0x00060D, 0x000810,
- 0x280C3A, 0x00008D, 0x000810, 0x28143A,
- 0x0A808D, 0x000820, 0x0002F5, 0x010040,
- 0x220007, 0x001275, 0x030042, 0x21004A,
- 0x00008D, 0x1A0944, 0x000007, 0x01980D,
- 0x000810, 0x08043A, 0x2B2206, 0x000007,
- 0x0001F5, 0x030042, 0x0D004A, 0x10000A,
- 0x089144, 0x000007, 0x000820, 0x010040,
- 0x0025F5, 0x0A3144, 0x000007, 0x000820,
- 0x032860, 0x030040, 0x00217D, 0x038042,
- 0x0B804A, 0x10000A, 0x000820, 0x031060,
- 0x030040, 0x00008D, 0x000124, 0x00012C,
- 0x000E64, 0x001A64, 0x00636C, 0x08010A,
- 0x10012A, 0x000820, 0x031060, 0x030040,
- 0x0020FD, 0x018042, 0x08000A, 0x00227D,
- 0x018042, 0x10000A, 0x000820, 0x031060,
- 0x030040, 0x00197D, 0x018042, 0x08000A,
- 0x0022FD, 0x038042, 0x10000A, 0x000820,
- 0x031060, 0x030040, 0x090D04, 0x000007,
- 0x000820, 0x030040, 0x038042, 0x0B804A,
- 0x10000A, 0x000820, 0x031060, 0x030040,
- 0x038042, 0x13804A, 0x19804A, 0x110D04,
- 0x198D04, 0x000007, 0x08000A, 0x001020,
- 0x031860, 0x030860, 0x030040, 0x00008D,
- 0x0B0944, 0x000007, 0x000820, 0x010040,
- 0x0005F5, 0x030042, 0x08000A, 0x000820,
- 0x010040, 0x0000F5, 0x010042, 0x08000A,
- 0x000904, 0x1C6086, 0x001E75, 0x030042,
- 0x01044A, 0x000C0A, 0x1C7206, 0x000007,
- 0x000402, 0x000C02, 0x00177D, 0x001AF5,
- 0x018042, 0x03144A, 0x031C4A, 0x03244A,
- 0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
- 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
- 0x030042, 0x0B004A, 0x1B804A, 0x13804A,
- 0x20000A, 0x089144, 0x19A144, 0x0389E4,
- 0x0399EC, 0x005502, 0x005D0A, 0x030042,
- 0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
- 0x089144, 0x19A144, 0x0389E4, 0x0399EC,
- 0x006502, 0x006D0A, 0x030042, 0x0B004A,
- 0x19004A, 0x2B804A, 0x13804A, 0x21804A,
- 0x30000A, 0x089144, 0x19A144, 0x2AB144,
- 0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
- 0x03A9E4, 0x000702, 0x00107D, 0x000415,
- 0x018042, 0x08000A, 0x0109E4, 0x000F02,
- 0x002AF5, 0x0019FD, 0x010042, 0x09804A,
- 0x10000A, 0x000934, 0x001674, 0x0029F5,
- 0x010042, 0x10000A, 0x00917C, 0x002075,
- 0x010042, 0x08000A, 0x000904, 0x1ED286,
- 0x0026F5, 0x0027F5, 0x030042, 0x09004A,
- 0x10000A, 0x000A3C, 0x00167C, 0x001A75,
- 0x000BFD, 0x010042, 0x51804A, 0x48000A,
- 0x160007, 0x001075, 0x010042, 0x282C0A,
- 0x281D12, 0x282512, 0x001F32, 0x1E0007,
- 0x0E0007, 0x001975, 0x010042, 0x002DF5,
- 0x0D004A, 0x10000A, 0x009144, 0x1FB286,
- 0x010042, 0x28340A, 0x000E5D, 0x00008D,
- 0x000375, 0x000820, 0x010040, 0x05D2F4,
- 0x54D104, 0x00735C, 0x205386, 0x000007,
- 0x0C0007, 0x080007, 0x0A0007, 0x02040D,
- 0x000810, 0x08043A, 0x332206, 0x000007,
- 0x205A06, 0x000007, 0x080007, 0x002275,
- 0x010042, 0x20000A, 0x002104, 0x212086,
- 0x001E2D, 0x0002F5, 0x010042, 0x08000A,
- 0x000904, 0x209286, 0x000007, 0x002010,
- 0x30043A, 0x00057D, 0x0180C3, 0x08000A,
- 0x028924, 0x280502, 0x280C02, 0x0A810D,
- 0x000820, 0x0002F5, 0x010040, 0x220007,
- 0x0004FD, 0x018042, 0x70000A, 0x030000,
- 0x007020, 0x06FA06, 0x018040, 0x02180D,
- 0x000810, 0x08043A, 0x2B2206, 0x000007,
- 0x0002FD, 0x018042, 0x08000A, 0x000904,
- 0x218A86, 0x000007, 0x01F206, 0x000007,
- 0x000875, 0x0009FD, 0x00010D, 0x220A06,
- 0x000295, 0x000B75, 0x00097D, 0x00000D,
- 0x000515, 0x010042, 0x18000A, 0x001904,
- 0x287886, 0x0006F5, 0x001020, 0x010040,
- 0x0004F5, 0x000820, 0x010040, 0x000775,
- 0x010042, 0x09804A, 0x10000A, 0x001124,
- 0x000904, 0x22BA86, 0x000815, 0x080102,
- 0x101204, 0x22DA06, 0x000575, 0x081204,
- 0x000007, 0x100102, 0x000575, 0x000425,
- 0x021124, 0x100102, 0x000820, 0x031060,
- 0x010040, 0x001924, 0x287886, 0x00008D,
- 0x000464, 0x009D04, 0x278886, 0x180102,
- 0x000575, 0x010042, 0x28040A, 0x00018D,
- 0x000924, 0x280D02, 0x00000D, 0x000924,
- 0x281502, 0x10000D, 0x000820, 0x0002F5,
- 0x010040, 0x200007, 0x001175, 0x0002FD,
- 0x018042, 0x08000A, 0x000904, 0x23C286,
- 0x000007, 0x000100, 0x080B20, 0x130B60,
- 0x1B0B60, 0x030A60, 0x010040, 0x050042,
- 0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
- 0x0006F5, 0x010042, 0x28140A, 0x0004F5,
- 0x010042, 0x08000A, 0x000315, 0x010D04,
- 0x24CA86, 0x004015, 0x000095, 0x010D04,
- 0x24B886, 0x100022, 0x10002A, 0x24E206,
- 0x000007, 0x333104, 0x2AA904, 0x000007,
- 0x032124, 0x280502, 0x001124, 0x000424,
- 0x000424, 0x003224, 0x00292C, 0x00636C,
- 0x25F386, 0x000007, 0x02B164, 0x000464,
- 0x000464, 0x00008D, 0x000A64, 0x280D02,
- 0x10008D, 0x000820, 0x0002F5, 0x010040,
- 0x220007, 0x00008D, 0x38B904, 0x000007,
- 0x03296C, 0x30010A, 0x0002F5, 0x010042,
- 0x08000A, 0x000904, 0x25BA86, 0x000007,
- 0x02312C, 0x28050A, 0x00008D, 0x01096C,
- 0x280D0A, 0x10010D, 0x000820, 0x0002F5,
- 0x010040, 0x220007, 0x001124, 0x000424,
- 0x000424, 0x003224, 0x300102, 0x032944,
- 0x267A86, 0x000007, 0x300002, 0x0004F5,
- 0x010042, 0x08000A, 0x000315, 0x010D04,
- 0x26C086, 0x003124, 0x000464, 0x300102,
- 0x0002F5, 0x010042, 0x08000A, 0x000904,
- 0x26CA86, 0x000007, 0x003124, 0x300502,
- 0x003924, 0x300583, 0x000883, 0x0005F5,
- 0x010042, 0x28040A, 0x00008D, 0x008124,
- 0x280D02, 0x00008D, 0x008124, 0x281502,
- 0x10018D, 0x000820, 0x0002F5, 0x010040,
- 0x220007, 0x001025, 0x000575, 0x030042,
- 0x09004A, 0x10000A, 0x0A0904, 0x121104,
- 0x000007, 0x001020, 0x050860, 0x050040,
- 0x0006FD, 0x018042, 0x09004A, 0x10000A,
- 0x0000A5, 0x0A0904, 0x121104, 0x000007,
- 0x000820, 0x019060, 0x010040, 0x0002F5,
- 0x010042, 0x08000A, 0x000904, 0x284286,
- 0x000007, 0x230A06, 0x000007, 0x000606,
- 0x000007, 0x0002F5, 0x010042, 0x08000A,
- 0x000904, 0x289286, 0x000007, 0x000100,
- 0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
- 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
- 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
- 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
- 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
- 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
- 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
- 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
- 0x000606, 0x018040, 0x00008D, 0x000A64,
- 0x280D02, 0x000A24, 0x00027D, 0x018042,
- 0x10000A, 0x001224, 0x0003FD, 0x018042,
- 0x08000A, 0x000904, 0x2A8286, 0x000007,
- 0x00018D, 0x000A24, 0x000464, 0x000464,
- 0x080102, 0x000924, 0x000424, 0x000424,
- 0x100102, 0x02000D, 0x009144, 0x2AD986,
- 0x000007, 0x0001FD, 0x018042, 0x08000A,
- 0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
- 0x000820, 0x0002FD, 0x018040, 0x200007,
- 0x00027D, 0x001020, 0x000606, 0x018040,
- 0x0002F5, 0x010042, 0x08000A, 0x000904,
- 0x2B2A86, 0x000007, 0x00037D, 0x018042,
- 0x08000A, 0x000904, 0x2B5A86, 0x000007,
- 0x000075, 0x002E7D, 0x010042, 0x0B804A,
- 0x000020, 0x000904, 0x000686, 0x010040,
- 0x31844A, 0x30048B, 0x000883, 0x00008D,
- 0x000810, 0x28143A, 0x00008D, 0x000810,
- 0x280C3A, 0x000675, 0x010042, 0x08000A,
- 0x003815, 0x010924, 0x280502, 0x0B000D,
- 0x000820, 0x0002F5, 0x010040, 0x000606,
- 0x220007, 0x000464, 0x000464, 0x000606,
- 0x000007, 0x000134, 0x007F8D, 0x00093C,
- 0x281D12, 0x282512, 0x001F32, 0x0E0007,
- 0x00010D, 0x00037D, 0x000820, 0x018040,
- 0x05D2F4, 0x000007, 0x080007, 0x00037D,
- 0x018042, 0x08000A, 0x000904, 0x2D0286,
- 0x000007, 0x000606, 0x000007, 0x000007,
- 0x000012, 0x100007, 0x320007, 0x600007,
- 0x100080, 0x48001A, 0x004904, 0x2D6186,
- 0x000007, 0x001210, 0x58003A, 0x000145,
- 0x5C5D04, 0x000007, 0x000080, 0x48001A,
- 0x004904, 0x2DB186, 0x000007, 0x001210,
- 0x50003A, 0x005904, 0x2E0886, 0x000045,
- 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
- 0x004224, 0x500102, 0x200502, 0x000082,
- 0x40001A, 0x004104, 0x2E3986, 0x000007,
- 0x003865, 0x40001A, 0x004020, 0x00104D,
- 0x04C184, 0x301B86, 0x000040, 0x040007,
- 0x000165, 0x000145, 0x004020, 0x000040,
- 0x000765, 0x080080, 0x40001A, 0x004104,
- 0x2EC986, 0x000007, 0x001210, 0x40003A,
- 0x004104, 0x2F2286, 0x00004D, 0x0000CD,
- 0x004810, 0x20043A, 0x000882, 0x40001A,
- 0x004104, 0x2F3186, 0x000007, 0x004820,
- 0x005904, 0x300886, 0x000040, 0x0007E5,
- 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
- 0x4216E0, 0x021260, 0x000040, 0x000032,
- 0x400075, 0x00007D, 0x07D574, 0x200512,
- 0x000082, 0x40001A, 0x004104, 0x2FE186,
- 0x000007, 0x037206, 0x640007, 0x060007,
- 0x0000E5, 0x000020, 0x000040, 0x000A65,
- 0x000020, 0x020040, 0x020040, 0x000040,
- 0x000165, 0x000042, 0x70000A, 0x007104,
- 0x30A286, 0x000007, 0x018206, 0x640007,
- 0x050000, 0x007020, 0x000040, 0x037206,
- 0x640007, 0x000007, 0x00306D, 0x028860,
- 0x029060, 0x08000A, 0x028860, 0x008040,
- 0x100012, 0x00100D, 0x009184, 0x314186,
- 0x000E0D, 0x009184, 0x325186, 0x000007,
- 0x300007, 0x001020, 0x003B6D, 0x008040,
- 0x000080, 0x08001A, 0x000904, 0x316186,
- 0x000007, 0x001220, 0x000DED, 0x008040,
- 0x008042, 0x10000A, 0x40000D, 0x109544,
- 0x000007, 0x001020, 0x000DED, 0x008040,
- 0x008042, 0x20040A, 0x000082, 0x08001A,
- 0x000904, 0x31F186, 0x000007, 0x003B6D,
- 0x008042, 0x08000A, 0x000E15, 0x010984,
- 0x329B86, 0x600007, 0x08001A, 0x000C15,
- 0x010984, 0x328386, 0x000020, 0x1A0007,
- 0x0002ED, 0x008040, 0x620007, 0x00306D,
- 0x028042, 0x0A804A, 0x000820, 0x0A804A,
- 0x000606, 0x10804A, 0x000007, 0x282512,
- 0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
- 0x000786, 0x000007, 0x0C0007, 0x0A0007,
- 0x1C0007, 0x003465, 0x020040, 0x004820,
- 0x025060, 0x40000A, 0x024060, 0x000040,
- 0x454944, 0x000007, 0x004020, 0x003AE5,
- 0x000040, 0x0028E5, 0x000042, 0x48000A,
- 0x004904, 0x386886, 0x002C65, 0x000042,
- 0x40000A, 0x0000D5, 0x454104, 0x000007,
- 0x000655, 0x054504, 0x34F286, 0x0001D5,
- 0x054504, 0x34F086, 0x002B65, 0x000042,
- 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
- 0x000007, 0x454504, 0x000007, 0x0000CD,
- 0x444944, 0x000007, 0x454504, 0x000007,
- 0x00014D, 0x554944, 0x000007, 0x045144,
- 0x34E986, 0x002C65, 0x000042, 0x48000A,
- 0x4CD104, 0x000007, 0x04C144, 0x34F386,
- 0x000007, 0x160007, 0x002CE5, 0x040042,
- 0x40000A, 0x004020, 0x000040, 0x002965,
- 0x000042, 0x40000A, 0x004104, 0x356086,
- 0x000007, 0x002402, 0x36A206, 0x005C02,
- 0x0025E5, 0x000042, 0x40000A, 0x004274,
- 0x002AE5, 0x000042, 0x40000A, 0x004274,
- 0x500112, 0x0029E5, 0x000042, 0x40000A,
- 0x004234, 0x454104, 0x000007, 0x004020,
- 0x000040, 0x003EE5, 0x000020, 0x000040,
- 0x002DE5, 0x400152, 0x50000A, 0x045144,
- 0x364A86, 0x0000C5, 0x003EE5, 0x004020,
- 0x000040, 0x002BE5, 0x000042, 0x40000A,
- 0x404254, 0x000007, 0x002AE5, 0x004020,
- 0x000040, 0x500132, 0x040134, 0x005674,
- 0x0029E5, 0x020042, 0x42000A, 0x000042,
- 0x50000A, 0x05417C, 0x0028E5, 0x000042,
- 0x48000A, 0x0000C5, 0x4CC144, 0x371086,
- 0x0026E5, 0x0027E5, 0x020042, 0x40004A,
- 0x50000A, 0x00423C, 0x00567C, 0x0028E5,
- 0x004820, 0x000040, 0x281D12, 0x282512,
- 0x001F72, 0x002965, 0x000042, 0x40000A,
- 0x004104, 0x37AA86, 0x0E0007, 0x160007,
- 0x1E0007, 0x003EE5, 0x000042, 0x40000A,
- 0x004104, 0x37E886, 0x002D65, 0x000042,
- 0x28340A, 0x003465, 0x020042, 0x42004A,
- 0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
- 0x54D104, 0x00735C, 0x385186, 0x000007,
- 0x000606, 0x080007, 0x0C0007, 0x080007,
- 0x0A0007, 0x0001E5, 0x020045, 0x004020,
- 0x000060, 0x000365, 0x000040, 0x002E65,
- 0x001A20, 0x0A1A60, 0x000040, 0x003465,
- 0x020042, 0x42004A, 0x004020, 0x4A004A,
- 0x000606, 0x50004A, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000
-};
-
-// --------------------------------------------
-// DS-1E Controller InstructionRAM Code
-// 1999/06/21
-// Buf441 slot is Enabled.
-// --------------------------------------------
-// 04/09 creat
-// 04/12 stop nise fix
-// 06/21 WorkingOff timming
-static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
- 0x000007, 0x240007, 0x0C0007, 0x1C0007,
- 0x060007, 0x700002, 0x000020, 0x030040,
- 0x007104, 0x004286, 0x030040, 0x000F0D,
- 0x000810, 0x20043A, 0x000282, 0x00020D,
- 0x000810, 0x20043A, 0x001282, 0x200E82,
- 0x00800D, 0x000810, 0x20043A, 0x001A82,
- 0x03460D, 0x000810, 0x10043A, 0x02EC0D,
- 0x000810, 0x18043A, 0x00010D, 0x020015,
- 0x0000FD, 0x000020, 0x038860, 0x039060,
- 0x038060, 0x038040, 0x038040, 0x038040,
- 0x018040, 0x000A7D, 0x038040, 0x038040,
- 0x018040, 0x200402, 0x000882, 0x08001A,
- 0x000904, 0x017186, 0x000007, 0x260007,
- 0x400007, 0x000007, 0x03258D, 0x000810,
- 0x18043A, 0x260007, 0x284402, 0x00087D,
- 0x018042, 0x00160A, 0x05A206, 0x000007,
- 0x440007, 0x00230D, 0x000810, 0x08043A,
- 0x22FA06, 0x000007, 0x0007FD, 0x018042,
- 0x08000A, 0x000904, 0x02AB86, 0x000195,
- 0x090D04, 0x000007, 0x000820, 0x0000F5,
- 0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
- 0x018040, 0x000A7D, 0x038042, 0x13804A,
- 0x18000A, 0x001820, 0x059060, 0x058860,
- 0x018040, 0x0000FD, 0x018042, 0x70000A,
- 0x000115, 0x071144, 0x033B86, 0x030000,
- 0x007020, 0x036206, 0x018040, 0x00360D,
- 0x000810, 0x08043A, 0x232206, 0x000007,
- 0x02EC0D, 0x000810, 0x18043A, 0x019A06,
- 0x000007, 0x240007, 0x000F8D, 0x000810,
- 0x00163A, 0x002402, 0x005C02, 0x0028FD,
- 0x000020, 0x018040, 0x08000D, 0x000815,
- 0x510984, 0x000007, 0x00004D, 0x000E5D,
- 0x000E02, 0x00430D, 0x000810, 0x08043A,
- 0x2E1206, 0x000007, 0x00008D, 0x000924,
- 0x000F02, 0x00470D, 0x000810, 0x08043A,
- 0x2E1206, 0x000007, 0x480480, 0x001210,
- 0x28043A, 0x00778D, 0x000810, 0x280C3A,
- 0x00068D, 0x000810, 0x28143A, 0x284402,
- 0x03258D, 0x000810, 0x18043A, 0x07FF8D,
- 0x000820, 0x0002FD, 0x018040, 0x260007,
- 0x200007, 0x0002FD, 0x018042, 0x08000A,
- 0x000904, 0x051286, 0x000007, 0x240007,
- 0x02EC0D, 0x000810, 0x18043A, 0x00387D,
- 0x018042, 0x08000A, 0x001015, 0x010984,
- 0x019B86, 0x000007, 0x01B206, 0x000007,
- 0x0008FD, 0x018042, 0x18000A, 0x001904,
- 0x22B886, 0x280007, 0x001810, 0x28043A,
- 0x280C02, 0x00000D, 0x000810, 0x28143A,
- 0x08808D, 0x000820, 0x0002FD, 0x018040,
- 0x200007, 0x00020D, 0x189904, 0x000007,
- 0x00402D, 0x0000BD, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x065A86, 0x000007,
- 0x000100, 0x000A20, 0x00047D, 0x018040,
- 0x018042, 0x20000A, 0x003015, 0x012144,
- 0x036186, 0x000007, 0x002104, 0x036186,
- 0x000007, 0x000F8D, 0x000810, 0x280C3A,
- 0x023944, 0x07C986, 0x000007, 0x001810,
- 0x28043A, 0x08810D, 0x000820, 0x0002FD,
- 0x018040, 0x200007, 0x002810, 0x78003A,
- 0x00788D, 0x000810, 0x08043A, 0x2A1206,
- 0x000007, 0x00400D, 0x001015, 0x189904,
- 0x292904, 0x393904, 0x000007, 0x070206,
- 0x000007, 0x0004F5, 0x00007D, 0x000020,
- 0x00008D, 0x010860, 0x018040, 0x00047D,
- 0x038042, 0x21804A, 0x18000A, 0x021944,
- 0x229086, 0x000007, 0x004075, 0x71F104,
- 0x000007, 0x010042, 0x28000A, 0x002904,
- 0x225886, 0x000007, 0x003C0D, 0x30A904,
- 0x000007, 0x00077D, 0x018042, 0x08000A,
- 0x000904, 0x08DA86, 0x00057D, 0x002820,
- 0x03B060, 0x08F206, 0x018040, 0x003020,
- 0x03A860, 0x018040, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x08FA86, 0x000007,
- 0x00057D, 0x018042, 0x28040A, 0x000E8D,
- 0x000810, 0x280C3A, 0x00000D, 0x000810,
- 0x28143A, 0x09000D, 0x000820, 0x0002FD,
- 0x018040, 0x200007, 0x003DFD, 0x000020,
- 0x018040, 0x00107D, 0x009D8D, 0x000810,
- 0x08043A, 0x2A1206, 0x000007, 0x000815,
- 0x08001A, 0x010984, 0x0A5186, 0x00137D,
- 0x200500, 0x280F20, 0x338F60, 0x3B8F60,
- 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
- 0x038A60, 0x018040, 0x00107D, 0x018042,
- 0x08000A, 0x000215, 0x010984, 0x3A8186,
- 0x000007, 0x007FBD, 0x383DC4, 0x000007,
- 0x001A7D, 0x001375, 0x018042, 0x09004A,
- 0x10000A, 0x0B8D04, 0x139504, 0x000007,
- 0x000820, 0x019060, 0x001104, 0x225886,
- 0x010040, 0x0017FD, 0x018042, 0x08000A,
- 0x000904, 0x225A86, 0x000007, 0x00197D,
- 0x038042, 0x09804A, 0x10000A, 0x000924,
- 0x001664, 0x0011FD, 0x038042, 0x2B804A,
- 0x19804A, 0x00008D, 0x218944, 0x000007,
- 0x002244, 0x0C1986, 0x000007, 0x001A64,
- 0x002A24, 0x00197D, 0x080102, 0x100122,
- 0x000820, 0x039060, 0x018040, 0x003DFD,
- 0x00008D, 0x000820, 0x018040, 0x001375,
- 0x001A7D, 0x010042, 0x09804A, 0x10000A,
- 0x00021D, 0x0189E4, 0x2992E4, 0x309144,
- 0x000007, 0x00060D, 0x000A15, 0x000C1D,
- 0x001025, 0x00A9E4, 0x012BE4, 0x000464,
- 0x01B3E4, 0x0232E4, 0x000464, 0x000464,
- 0x000464, 0x000464, 0x00040D, 0x08B1C4,
- 0x000007, 0x000820, 0x000BF5, 0x030040,
- 0x00197D, 0x038042, 0x09804A, 0x000A24,
- 0x08000A, 0x080E64, 0x000007, 0x100122,
- 0x000820, 0x031060, 0x010040, 0x0064AC,
- 0x00027D, 0x000020, 0x018040, 0x00107D,
- 0x018042, 0x0011FD, 0x3B804A, 0x09804A,
- 0x20000A, 0x000095, 0x1A1144, 0x00A144,
- 0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
- 0x0018FD, 0x018042, 0x0010FD, 0x09804A,
- 0x28000A, 0x000095, 0x010924, 0x002A64,
- 0x0E4986, 0x000007, 0x002904, 0x0E5A86,
- 0x000007, 0x0E6206, 0x080002, 0x00008D,
- 0x00387D, 0x000820, 0x018040, 0x00127D,
- 0x018042, 0x10000A, 0x003904, 0x0F0986,
- 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
- 0x000025, 0x0FB206, 0x00002D, 0x000015,
- 0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
- 0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
- 0x400025, 0x00008D, 0x110944, 0x000007,
- 0x00018D, 0x109504, 0x000007, 0x009164,
- 0x000424, 0x000424, 0x000424, 0x100102,
- 0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
- 0x00018D, 0x00042D, 0x00008D, 0x109504,
- 0x000007, 0x00020D, 0x109184, 0x000007,
- 0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
- 0x018040, 0x003BFD, 0x001020, 0x03A860,
- 0x000815, 0x313184, 0x212184, 0x000007,
- 0x03B060, 0x03A060, 0x018040, 0x0022FD,
- 0x000095, 0x010924, 0x000424, 0x000424,
- 0x001264, 0x100102, 0x000820, 0x039060,
- 0x018040, 0x001924, 0x010F0D, 0x00397D,
- 0x000820, 0x058040, 0x038042, 0x09844A,
- 0x000606, 0x08040A, 0x000424, 0x000424,
- 0x00117D, 0x018042, 0x08000A, 0x000A24,
- 0x280502, 0x280C02, 0x09800D, 0x000820,
- 0x0002FD, 0x018040, 0x200007, 0x0022FD,
- 0x018042, 0x08000A, 0x000095, 0x280DC4,
- 0x011924, 0x00197D, 0x018042, 0x0011FD,
- 0x09804A, 0x10000A, 0x0000B5, 0x113144,
- 0x0A8D04, 0x000007, 0x080A44, 0x129504,
- 0x000007, 0x0023FD, 0x001020, 0x038040,
- 0x101244, 0x000007, 0x000820, 0x039060,
- 0x018040, 0x0002FD, 0x018042, 0x08000A,
- 0x000904, 0x123286, 0x000007, 0x003BFD,
- 0x000100, 0x000A10, 0x0B807A, 0x13804A,
- 0x090984, 0x000007, 0x000095, 0x013D04,
- 0x12B886, 0x10000A, 0x100002, 0x090984,
- 0x000007, 0x038042, 0x11804A, 0x090D04,
- 0x000007, 0x10000A, 0x090D84, 0x000007,
- 0x00257D, 0x000820, 0x018040, 0x00010D,
- 0x000810, 0x28143A, 0x00127D, 0x018042,
- 0x20000A, 0x00197D, 0x018042, 0x00117D,
- 0x31804A, 0x10000A, 0x003124, 0x013B8D,
- 0x00397D, 0x000820, 0x058040, 0x038042,
- 0x09844A, 0x000606, 0x08040A, 0x300102,
- 0x003124, 0x000424, 0x000424, 0x001224,
- 0x280502, 0x001A4C, 0x143986, 0x700002,
- 0x00002D, 0x030000, 0x00387D, 0x018042,
- 0x10000A, 0x146206, 0x002124, 0x0000AD,
- 0x100002, 0x00010D, 0x000924, 0x006B24,
- 0x014A0D, 0x00397D, 0x000820, 0x058040,
- 0x038042, 0x09844A, 0x000606, 0x08040A,
- 0x003264, 0x00008D, 0x000A24, 0x001020,
- 0x00227D, 0x018040, 0x014F8D, 0x000810,
- 0x08043A, 0x2B5A06, 0x000007, 0x002820,
- 0x00207D, 0x018040, 0x00117D, 0x038042,
- 0x13804A, 0x33800A, 0x00387D, 0x018042,
- 0x08000A, 0x000904, 0x177286, 0x000007,
- 0x00008D, 0x030964, 0x015B0D, 0x00397D,
- 0x000820, 0x058040, 0x038042, 0x09844A,
- 0x000606, 0x08040A, 0x380102, 0x000424,
- 0x000424, 0x001224, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x15DA86, 0x000007,
- 0x280502, 0x001A4C, 0x177186, 0x000007,
- 0x032164, 0x00632C, 0x003DFD, 0x018042,
- 0x08000A, 0x000095, 0x090904, 0x000007,
- 0x000820, 0x001A4C, 0x169986, 0x018040,
- 0x030000, 0x16B206, 0x002124, 0x00010D,
- 0x000924, 0x006B24, 0x016F0D, 0x00397D,
- 0x000820, 0x058040, 0x038042, 0x09844A,
- 0x000606, 0x08040A, 0x003A64, 0x000095,
- 0x001224, 0x0002FD, 0x018042, 0x08000A,
- 0x000904, 0x171286, 0x000007, 0x01760D,
- 0x000810, 0x08043A, 0x2B5A06, 0x000007,
- 0x160A06, 0x000007, 0x007020, 0x08010A,
- 0x10012A, 0x0020FD, 0x038860, 0x039060,
- 0x018040, 0x00227D, 0x018042, 0x003DFD,
- 0x08000A, 0x31844A, 0x000904, 0x181086,
- 0x18008B, 0x00008D, 0x189904, 0x00312C,
- 0x18E206, 0x000007, 0x00324C, 0x186B86,
- 0x000007, 0x001904, 0x186886, 0x000007,
- 0x000095, 0x199144, 0x00222C, 0x003124,
- 0x00636C, 0x000E3D, 0x001375, 0x000BFD,
- 0x010042, 0x09804A, 0x10000A, 0x038AEC,
- 0x0393EC, 0x00224C, 0x18E186, 0x000007,
- 0x00008D, 0x189904, 0x00226C, 0x00322C,
- 0x30050A, 0x301DAB, 0x002083, 0x0018FD,
- 0x018042, 0x08000A, 0x018924, 0x300502,
- 0x001083, 0x001875, 0x010042, 0x10000A,
- 0x00008D, 0x010924, 0x001375, 0x330542,
- 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
- 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
- 0x006083, 0x0002F5, 0x010042, 0x08000A,
- 0x000904, 0x19B286, 0x000007, 0x001E2D,
- 0x0005FD, 0x018042, 0x08000A, 0x028924,
- 0x280502, 0x00060D, 0x000810, 0x280C3A,
- 0x00008D, 0x000810, 0x28143A, 0x0A808D,
- 0x000820, 0x0002F5, 0x010040, 0x220007,
- 0x001275, 0x030042, 0x21004A, 0x00008D,
- 0x1A0944, 0x000007, 0x01AB8D, 0x000810,
- 0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
- 0x030042, 0x0D004A, 0x10000A, 0x089144,
- 0x000007, 0x000820, 0x010040, 0x0025F5,
- 0x0A3144, 0x000007, 0x000820, 0x032860,
- 0x030040, 0x00217D, 0x038042, 0x0B804A,
- 0x10000A, 0x000820, 0x031060, 0x030040,
- 0x00008D, 0x000124, 0x00012C, 0x000E64,
- 0x001A64, 0x00636C, 0x08010A, 0x10012A,
- 0x000820, 0x031060, 0x030040, 0x0020FD,
- 0x018042, 0x08000A, 0x00227D, 0x018042,
- 0x10000A, 0x000820, 0x031060, 0x030040,
- 0x00197D, 0x018042, 0x08000A, 0x0022FD,
- 0x038042, 0x10000A, 0x000820, 0x031060,
- 0x030040, 0x090D04, 0x000007, 0x000820,
- 0x030040, 0x038042, 0x0B804A, 0x10000A,
- 0x000820, 0x031060, 0x030040, 0x038042,
- 0x13804A, 0x19804A, 0x110D04, 0x198D04,
- 0x000007, 0x08000A, 0x001020, 0x031860,
- 0x030860, 0x030040, 0x00008D, 0x0B0944,
- 0x000007, 0x000820, 0x010040, 0x0005F5,
- 0x030042, 0x08000A, 0x000820, 0x010040,
- 0x0000F5, 0x010042, 0x08000A, 0x000904,
- 0x1D9886, 0x001E75, 0x030042, 0x01044A,
- 0x000C0A, 0x1DAA06, 0x000007, 0x000402,
- 0x000C02, 0x00177D, 0x001AF5, 0x018042,
- 0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
- 0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
- 0x00043D, 0x0013F5, 0x001AFD, 0x030042,
- 0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
- 0x089144, 0x19A144, 0x0389E4, 0x0399EC,
- 0x005502, 0x005D0A, 0x030042, 0x0B004A,
- 0x1B804A, 0x13804A, 0x20000A, 0x089144,
- 0x19A144, 0x0389E4, 0x0399EC, 0x006502,
- 0x006D0A, 0x030042, 0x0B004A, 0x19004A,
- 0x2B804A, 0x13804A, 0x21804A, 0x30000A,
- 0x089144, 0x19A144, 0x2AB144, 0x0389E4,
- 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
- 0x000702, 0x00107D, 0x000415, 0x018042,
- 0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
- 0x0019FD, 0x010042, 0x09804A, 0x10000A,
- 0x000934, 0x001674, 0x0029F5, 0x010042,
- 0x10000A, 0x00917C, 0x002075, 0x010042,
- 0x08000A, 0x000904, 0x200A86, 0x0026F5,
- 0x0027F5, 0x030042, 0x09004A, 0x10000A,
- 0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
- 0x010042, 0x51804A, 0x48000A, 0x160007,
- 0x001075, 0x010042, 0x282C0A, 0x281D12,
- 0x282512, 0x001F32, 0x1E0007, 0x0E0007,
- 0x001975, 0x010042, 0x002DF5, 0x0D004A,
- 0x10000A, 0x009144, 0x20EA86, 0x010042,
- 0x28340A, 0x000E5D, 0x00008D, 0x000375,
- 0x000820, 0x010040, 0x05D2F4, 0x54D104,
- 0x00735C, 0x218B86, 0x000007, 0x0C0007,
- 0x080007, 0x0A0007, 0x02178D, 0x000810,
- 0x08043A, 0x34B206, 0x000007, 0x219206,
- 0x000007, 0x080007, 0x002275, 0x010042,
- 0x20000A, 0x002104, 0x225886, 0x001E2D,
- 0x0002F5, 0x010042, 0x08000A, 0x000904,
- 0x21CA86, 0x000007, 0x002010, 0x30043A,
- 0x00057D, 0x0180C3, 0x08000A, 0x028924,
- 0x280502, 0x280C02, 0x0A810D, 0x000820,
- 0x0002F5, 0x010040, 0x220007, 0x0004FD,
- 0x018042, 0x70000A, 0x030000, 0x007020,
- 0x07FA06, 0x018040, 0x022B8D, 0x000810,
- 0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
- 0x018042, 0x08000A, 0x000904, 0x22C286,
- 0x000007, 0x020206, 0x000007, 0x000875,
- 0x0009FD, 0x00010D, 0x234206, 0x000295,
- 0x000B75, 0x00097D, 0x00000D, 0x000515,
- 0x010042, 0x18000A, 0x001904, 0x2A0086,
- 0x0006F5, 0x001020, 0x010040, 0x0004F5,
- 0x000820, 0x010040, 0x000775, 0x010042,
- 0x09804A, 0x10000A, 0x001124, 0x000904,
- 0x23F286, 0x000815, 0x080102, 0x101204,
- 0x241206, 0x000575, 0x081204, 0x000007,
- 0x100102, 0x000575, 0x000425, 0x021124,
- 0x100102, 0x000820, 0x031060, 0x010040,
- 0x001924, 0x2A0086, 0x00008D, 0x000464,
- 0x009D04, 0x291086, 0x180102, 0x000575,
- 0x010042, 0x28040A, 0x00018D, 0x000924,
- 0x280D02, 0x00000D, 0x000924, 0x281502,
- 0x10000D, 0x000820, 0x0002F5, 0x010040,
- 0x200007, 0x001175, 0x0002FD, 0x018042,
- 0x08000A, 0x000904, 0x24FA86, 0x000007,
- 0x000100, 0x080B20, 0x130B60, 0x1B0B60,
- 0x030A60, 0x010040, 0x050042, 0x3D004A,
- 0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
- 0x010042, 0x28140A, 0x0004F5, 0x010042,
- 0x08000A, 0x000315, 0x010D04, 0x260286,
- 0x004015, 0x000095, 0x010D04, 0x25F086,
- 0x100022, 0x10002A, 0x261A06, 0x000007,
- 0x333104, 0x2AA904, 0x000007, 0x032124,
- 0x280502, 0x284402, 0x001124, 0x400102,
- 0x000424, 0x000424, 0x003224, 0x00292C,
- 0x00636C, 0x277386, 0x000007, 0x02B164,
- 0x000464, 0x000464, 0x00008D, 0x000A64,
- 0x280D02, 0x10008D, 0x000820, 0x0002F5,
- 0x010040, 0x220007, 0x00008D, 0x38B904,
- 0x000007, 0x03296C, 0x30010A, 0x0002F5,
- 0x010042, 0x08000A, 0x000904, 0x270286,
- 0x000007, 0x00212C, 0x28050A, 0x00316C,
- 0x00046C, 0x00046C, 0x28450A, 0x001124,
- 0x006B64, 0x100102, 0x00008D, 0x01096C,
- 0x280D0A, 0x10010D, 0x000820, 0x0002F5,
- 0x010040, 0x220007, 0x004124, 0x000424,
- 0x000424, 0x003224, 0x300102, 0x032944,
- 0x27FA86, 0x000007, 0x300002, 0x0004F5,
- 0x010042, 0x08000A, 0x000315, 0x010D04,
- 0x284086, 0x003124, 0x000464, 0x300102,
- 0x0002F5, 0x010042, 0x08000A, 0x000904,
- 0x284A86, 0x000007, 0x284402, 0x003124,
- 0x300502, 0x003924, 0x300583, 0x000883,
- 0x0005F5, 0x010042, 0x28040A, 0x00008D,
- 0x008124, 0x280D02, 0x00008D, 0x008124,
- 0x281502, 0x10018D, 0x000820, 0x0002F5,
- 0x010040, 0x220007, 0x001025, 0x000575,
- 0x030042, 0x09004A, 0x10000A, 0x0A0904,
- 0x121104, 0x000007, 0x001020, 0x050860,
- 0x050040, 0x0006FD, 0x018042, 0x09004A,
- 0x10000A, 0x0000A5, 0x0A0904, 0x121104,
- 0x000007, 0x000820, 0x019060, 0x010040,
- 0x0002F5, 0x010042, 0x08000A, 0x000904,
- 0x29CA86, 0x000007, 0x244206, 0x000007,
- 0x000606, 0x000007, 0x0002F5, 0x010042,
- 0x08000A, 0x000904, 0x2A1A86, 0x000007,
- 0x000100, 0x080B20, 0x138B60, 0x1B8B60,
- 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
- 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
- 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
- 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
- 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
- 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
- 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
- 0x038A60, 0x000606, 0x018040, 0x00008D,
- 0x000A64, 0x280D02, 0x000A24, 0x00027D,
- 0x018042, 0x10000A, 0x001224, 0x0003FD,
- 0x018042, 0x08000A, 0x000904, 0x2C0A86,
- 0x000007, 0x00018D, 0x000A24, 0x000464,
- 0x000464, 0x080102, 0x000924, 0x000424,
- 0x000424, 0x100102, 0x02000D, 0x009144,
- 0x2C6186, 0x000007, 0x0001FD, 0x018042,
- 0x08000A, 0x000A44, 0x2C4386, 0x018042,
- 0x0A000D, 0x000820, 0x0002FD, 0x018040,
- 0x200007, 0x00027D, 0x001020, 0x000606,
- 0x018040, 0x0002F5, 0x010042, 0x08000A,
- 0x000904, 0x2CB286, 0x000007, 0x00037D,
- 0x018042, 0x08000A, 0x000904, 0x2CE286,
- 0x000007, 0x000075, 0x002E7D, 0x010042,
- 0x0B804A, 0x000020, 0x000904, 0x000686,
- 0x010040, 0x31844A, 0x30048B, 0x000883,
- 0x00008D, 0x000810, 0x28143A, 0x00008D,
- 0x000810, 0x280C3A, 0x000675, 0x010042,
- 0x08000A, 0x003815, 0x010924, 0x280502,
- 0x0B000D, 0x000820, 0x0002F5, 0x010040,
- 0x000606, 0x220007, 0x000464, 0x000464,
- 0x000606, 0x000007, 0x000134, 0x007F8D,
- 0x00093C, 0x281D12, 0x282512, 0x001F32,
- 0x0E0007, 0x00010D, 0x00037D, 0x000820,
- 0x018040, 0x05D2F4, 0x000007, 0x080007,
- 0x00037D, 0x018042, 0x08000A, 0x000904,
- 0x2E8A86, 0x000007, 0x000606, 0x000007,
- 0x000007, 0x000012, 0x100007, 0x320007,
- 0x600007, 0x460007, 0x100080, 0x48001A,
- 0x004904, 0x2EF186, 0x000007, 0x001210,
- 0x58003A, 0x000145, 0x5C5D04, 0x000007,
- 0x000080, 0x48001A, 0x004904, 0x2F4186,
- 0x000007, 0x001210, 0x50003A, 0x005904,
- 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
- 0x7FFF7D, 0x07D524, 0x004224, 0x500102,
- 0x200502, 0x000082, 0x40001A, 0x004104,
- 0x2FC986, 0x000007, 0x003865, 0x40001A,
- 0x004020, 0x00104D, 0x04C184, 0x31AB86,
- 0x000040, 0x040007, 0x000165, 0x000145,
- 0x004020, 0x000040, 0x000765, 0x080080,
- 0x40001A, 0x004104, 0x305986, 0x000007,
- 0x001210, 0x40003A, 0x004104, 0x30B286,
- 0x00004D, 0x0000CD, 0x004810, 0x20043A,
- 0x000882, 0x40001A, 0x004104, 0x30C186,
- 0x000007, 0x004820, 0x005904, 0x319886,
- 0x000040, 0x0007E5, 0x200480, 0x2816A0,
- 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
- 0x000040, 0x000032, 0x400075, 0x00007D,
- 0x07D574, 0x200512, 0x000082, 0x40001A,
- 0x004104, 0x317186, 0x000007, 0x038A06,
- 0x640007, 0x0000E5, 0x000020, 0x000040,
- 0x000A65, 0x000020, 0x020040, 0x020040,
- 0x000040, 0x000165, 0x000042, 0x70000A,
- 0x007104, 0x323286, 0x000007, 0x060007,
- 0x019A06, 0x640007, 0x050000, 0x007020,
- 0x000040, 0x038A06, 0x640007, 0x000007,
- 0x00306D, 0x028860, 0x029060, 0x08000A,
- 0x028860, 0x008040, 0x100012, 0x00100D,
- 0x009184, 0x32D186, 0x000E0D, 0x009184,
- 0x33E186, 0x000007, 0x300007, 0x001020,
- 0x003B6D, 0x008040, 0x000080, 0x08001A,
- 0x000904, 0x32F186, 0x000007, 0x001220,
- 0x000DED, 0x008040, 0x008042, 0x10000A,
- 0x40000D, 0x109544, 0x000007, 0x001020,
- 0x000DED, 0x008040, 0x008042, 0x20040A,
- 0x000082, 0x08001A, 0x000904, 0x338186,
- 0x000007, 0x003B6D, 0x008042, 0x08000A,
- 0x000E15, 0x010984, 0x342B86, 0x600007,
- 0x08001A, 0x000C15, 0x010984, 0x341386,
- 0x000020, 0x1A0007, 0x0002ED, 0x008040,
- 0x620007, 0x00306D, 0x028042, 0x0A804A,
- 0x000820, 0x0A804A, 0x000606, 0x10804A,
- 0x000007, 0x282512, 0x001F32, 0x05D2F4,
- 0x54D104, 0x00735C, 0x000786, 0x000007,
- 0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
- 0x020040, 0x004820, 0x025060, 0x40000A,
- 0x024060, 0x000040, 0x454944, 0x000007,
- 0x004020, 0x003AE5, 0x000040, 0x0028E5,
- 0x000042, 0x48000A, 0x004904, 0x39F886,
- 0x002C65, 0x000042, 0x40000A, 0x0000D5,
- 0x454104, 0x000007, 0x000655, 0x054504,
- 0x368286, 0x0001D5, 0x054504, 0x368086,
- 0x002B65, 0x000042, 0x003AE5, 0x50004A,
- 0x40000A, 0x45C3D4, 0x000007, 0x454504,
- 0x000007, 0x0000CD, 0x444944, 0x000007,
- 0x454504, 0x000007, 0x00014D, 0x554944,
- 0x000007, 0x045144, 0x367986, 0x002C65,
- 0x000042, 0x48000A, 0x4CD104, 0x000007,
- 0x04C144, 0x368386, 0x000007, 0x160007,
- 0x002CE5, 0x040042, 0x40000A, 0x004020,
- 0x000040, 0x002965, 0x000042, 0x40000A,
- 0x004104, 0x36F086, 0x000007, 0x002402,
- 0x383206, 0x005C02, 0x0025E5, 0x000042,
- 0x40000A, 0x004274, 0x002AE5, 0x000042,
- 0x40000A, 0x004274, 0x500112, 0x0029E5,
- 0x000042, 0x40000A, 0x004234, 0x454104,
- 0x000007, 0x004020, 0x000040, 0x003EE5,
- 0x000020, 0x000040, 0x002DE5, 0x400152,
- 0x50000A, 0x045144, 0x37DA86, 0x0000C5,
- 0x003EE5, 0x004020, 0x000040, 0x002BE5,
- 0x000042, 0x40000A, 0x404254, 0x000007,
- 0x002AE5, 0x004020, 0x000040, 0x500132,
- 0x040134, 0x005674, 0x0029E5, 0x020042,
- 0x42000A, 0x000042, 0x50000A, 0x05417C,
- 0x0028E5, 0x000042, 0x48000A, 0x0000C5,
- 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
- 0x020042, 0x40004A, 0x50000A, 0x00423C,
- 0x00567C, 0x0028E5, 0x004820, 0x000040,
- 0x281D12, 0x282512, 0x001F72, 0x002965,
- 0x000042, 0x40000A, 0x004104, 0x393A86,
- 0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
- 0x000042, 0x40000A, 0x004104, 0x397886,
- 0x002D65, 0x000042, 0x28340A, 0x003465,
- 0x020042, 0x42004A, 0x004020, 0x4A004A,
- 0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
- 0x39E186, 0x000007, 0x000606, 0x080007,
- 0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
- 0x020045, 0x004020, 0x000060, 0x000365,
- 0x000040, 0x002E65, 0x001A20, 0x0A1A60,
- 0x000040, 0x003465, 0x020042, 0x42004A,
- 0x004020, 0x4A004A, 0x000606, 0x50004A,
- 0x0017FD, 0x018042, 0x08000A, 0x000904,
- 0x225A86, 0x000007, 0x00107D, 0x018042,
- 0x0011FD, 0x33804A, 0x19804A, 0x20000A,
- 0x000095, 0x2A1144, 0x01A144, 0x3B9086,
- 0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
- 0x018042, 0x0010FD, 0x09804A, 0x38000A,
- 0x000095, 0x010924, 0x003A64, 0x3B8186,
- 0x000007, 0x003904, 0x3B9286, 0x000007,
- 0x3B9A06, 0x00000D, 0x00008D, 0x000820,
- 0x00387D, 0x018040, 0x700002, 0x00117D,
- 0x018042, 0x00197D, 0x29804A, 0x30000A,
- 0x380002, 0x003124, 0x000424, 0x000424,
- 0x002A24, 0x280502, 0x00068D, 0x000810,
- 0x28143A, 0x00750D, 0x00B124, 0x002264,
- 0x3D0386, 0x284402, 0x000810, 0x280C3A,
- 0x0B800D, 0x000820, 0x0002FD, 0x018040,
- 0x200007, 0x00758D, 0x00B124, 0x100102,
- 0x012144, 0x3E4986, 0x001810, 0x10003A,
- 0x00387D, 0x018042, 0x08000A, 0x000904,
- 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
- 0x00008D, 0x023164, 0x000A64, 0x280D02,
- 0x0B808D, 0x000820, 0x0002FD, 0x018040,
- 0x200007, 0x00387D, 0x018042, 0x08000A,
- 0x000904, 0x3E3286, 0x030000, 0x0002FD,
- 0x018042, 0x08000A, 0x000904, 0x3D8286,
- 0x000007, 0x002810, 0x28043A, 0x00750D,
- 0x030924, 0x002264, 0x280D02, 0x02316C,
- 0x28450A, 0x0B810D, 0x000820, 0x0002FD,
- 0x018040, 0x200007, 0x00008D, 0x000A24,
- 0x3E4A06, 0x100102, 0x001810, 0x10003A,
- 0x0000BD, 0x003810, 0x30043A, 0x00187D,
- 0x018042, 0x0018FD, 0x09804A, 0x20000A,
- 0x0000AD, 0x028924, 0x07212C, 0x001010,
- 0x300583, 0x300D8B, 0x3014BB, 0x301C83,
- 0x002083, 0x00137D, 0x038042, 0x33844A,
- 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
- 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
- 0x001E0D, 0x0005FD, 0x018042, 0x20000A,
- 0x020924, 0x00068D, 0x00A96C, 0x00009D,
- 0x0002FD, 0x018042, 0x08000A, 0x000904,
- 0x3F6A86, 0x000007, 0x280502, 0x280D0A,
- 0x284402, 0x001810, 0x28143A, 0x0C008D,
- 0x000820, 0x0002FD, 0x018040, 0x220007,
- 0x003904, 0x225886, 0x001E0D, 0x00057D,
- 0x018042, 0x20000A, 0x020924, 0x0000A5,
- 0x0002FD, 0x018042, 0x08000A, 0x000904,
- 0x402A86, 0x000007, 0x280502, 0x280C02,
- 0x002010, 0x28143A, 0x0C010D, 0x000820,
- 0x0002FD, 0x018040, 0x225A06, 0x220007,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000,
- 0x000000, 0x000000, 0x000000, 0x000000
-};
-
-#endif //_HWMCODE_
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 29b3056c510..90d0d62bd0b 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -258,8 +259,10 @@ static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,
unsigned long flags;
int result;
- snd_assert(rvoice != NULL, return -EINVAL);
- snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);
+ if (snd_BUG_ON(!rvoice))
+ return -EINVAL;
+ if (snd_BUG_ON(pair && type != YMFPCI_PCM))
+ return -EINVAL;
spin_lock_irqsave(&chip->voice_lock, flags);
for (;;) {
@@ -277,7 +280,8 @@ static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voic
{
unsigned long flags;
- snd_assert(pvoice != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pvoice))
+ return -EINVAL;
snd_ymfpci_hw_stop(chip);
spin_lock_irqsave(&chip->voice_lock, flags);
if (pvoice->number == chip->src441_used) {
@@ -493,7 +497,8 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
u8 use_left, use_right;
unsigned long flags;
- snd_assert(voice != NULL, return);
+ if (snd_BUG_ON(!voice))
+ return;
if (runtime->channels == 1) {
use_left = 1;
use_right = 1;
@@ -1812,7 +1817,8 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
}
/* add S/PDIF control */
- snd_assert(chip->pcm_spdif != NULL, return -EIO);
+ if (snd_BUG_ON(!chip->pcm_spdif))
+ return -ENXIO;
if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)
return err;
kctl->id.device = chip->pcm_spdif->device;
@@ -1993,55 +1999,6 @@ static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip)
}
}
-#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
-
-#include "ymfpci_image.h"
-
-static struct firmware snd_ymfpci_dsp_microcode = {
- .size = YDSXG_DSPLENGTH,
- .data = (u8 *)DspInst,
-};
-static struct firmware snd_ymfpci_controller_microcode = {
- .size = YDSXG_CTRLLENGTH,
- .data = (u8 *)CntrlInst,
-};
-static struct firmware snd_ymfpci_controller_1e_microcode = {
- .size = YDSXG_CTRLLENGTH,
- .data = (u8 *)CntrlInst1E,
-};
-#endif
-
-#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
-static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
-{
- chip->dsp_microcode = &snd_ymfpci_dsp_microcode;
- if (chip->device_id == PCI_DEVICE_ID_YAMAHA_724F ||
- chip->device_id == PCI_DEVICE_ID_YAMAHA_740C ||
- chip->device_id == PCI_DEVICE_ID_YAMAHA_744 ||
- chip->device_id == PCI_DEVICE_ID_YAMAHA_754)
- chip->controller_microcode =
- &snd_ymfpci_controller_1e_microcode;
- else
- chip->controller_microcode =
- &snd_ymfpci_controller_microcode;
- return 0;
-}
-
-#else /* use fw_loader */
-
-#ifdef __LITTLE_ENDIAN
-static inline void snd_ymfpci_convert_from_le(const struct firmware *fw) { }
-#else
-static void snd_ymfpci_convert_from_le(const struct firmware *fw)
-{
- int i;
- u32 *data = (u32 *)fw->data;
-
- for (i = 0; i < fw->size / 4; ++i)
- le32_to_cpus(&data[i]);
-}
-#endif
-
static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
{
int err, is_1e;
@@ -2050,9 +2007,7 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
err = request_firmware(&chip->dsp_microcode, "yamaha/ds1_dsp.fw",
&chip->pci->dev);
if (err >= 0) {
- if (chip->dsp_microcode->size == YDSXG_DSPLENGTH)
- snd_ymfpci_convert_from_le(chip->dsp_microcode);
- else {
+ if (chip->dsp_microcode->size != YDSXG_DSPLENGTH) {
snd_printk(KERN_ERR "DSP microcode has wrong size\n");
err = -EINVAL;
}
@@ -2067,9 +2022,7 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
err = request_firmware(&chip->controller_microcode, name,
&chip->pci->dev);
if (err >= 0) {
- if (chip->controller_microcode->size == YDSXG_CTRLLENGTH)
- snd_ymfpci_convert_from_le(chip->controller_microcode);
- else {
+ if (chip->controller_microcode->size != YDSXG_CTRLLENGTH) {
snd_printk(KERN_ERR "controller microcode"
" has wrong size\n");
err = -EINVAL;
@@ -2084,13 +2037,11 @@ MODULE_FIRMWARE("yamaha/ds1_dsp.fw");
MODULE_FIRMWARE("yamaha/ds1_ctrl.fw");
MODULE_FIRMWARE("yamaha/ds1e_ctrl.fw");
-#endif
-
static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
{
int i;
u16 ctrl;
- u32 *inst;
+ const __le32 *inst;
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
snd_ymfpci_disable_dsp(chip);
@@ -2105,14 +2056,16 @@ static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
/* setup DSP instruction code */
- inst = (u32 *)chip->dsp_microcode->data;
+ inst = (const __le32 *)chip->dsp_microcode->data;
for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
- snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]);
+ snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2),
+ le32_to_cpu(inst[i]));
/* setup control instruction code */
- inst = (u32 *)chip->controller_microcode->data;
+ inst = (const __le32 *)chip->controller_microcode->data;
for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
- snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
+ snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2),
+ le32_to_cpu(inst[i]));
snd_ymfpci_enable_dsp(chip);
}
@@ -2185,7 +2138,8 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
chip->work_base = ptr;
chip->work_base_addr = ptr_addr;
- snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, );
+ snd_BUG_ON(ptr + chip->work_size !=
+ chip->work_ptr.area + chip->work_ptr.bytes);
snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);
snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr);
@@ -2205,6 +2159,7 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
for (reg = 0x80; reg < 0xc0; reg += 4)
snd_ymfpci_writel(chip, reg, 0);
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+ snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0x3fff3fff);
snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
@@ -2219,7 +2174,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip)
{
u16 ctrl;
- snd_assert(chip != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
if (chip->res_reg_area) { /* don't touch busy hardware */
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
@@ -2264,10 +2220,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip)
pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
pci_disable_device(chip->pci);
-#ifndef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
release_firmware(chip->dsp_microcode);
release_firmware(chip->controller_microcode);
-#endif
kfree(chip);
return 0;
}
@@ -2324,6 +2278,7 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
snd_ymfpci_disable_dsp(chip);
pci_disable_device(pci);
pci_save_state(pci);
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index c9fa1a2bc58..7fbb190adf6 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -1,11 +1,16 @@
# ALSA PCMCIA drivers
-menu "PCMCIA devices"
- depends on SND!=n && PCMCIA
+menuconfig SND_PCMCIA
+ bool "PCMCIA sound devices"
+ depends on PCMCIA
+ default y
+ help
+ Support for sound devices connected via the PCMCIA bus.
+
+if SND_PCMCIA && PCMCIA
config SND_VXPOCKET
tristate "Digigram VXpocket"
- depends on SND && PCMCIA
select SND_VX_LIB
help
Say Y here to include support for Digigram VXpocket and
@@ -16,7 +21,6 @@ config SND_VXPOCKET
config SND_PDAUDIOCF
tristate "Sound Core PDAudioCF"
- depends on SND && PCMCIA
select SND_PCM
help
Say Y here to include support for Sound Core PDAudioCF
@@ -25,4 +29,5 @@ config SND_PDAUDIOCF
To compile this driver as a module, choose M here: the module
will be called snd-pdaudiocf.
-endmenu
+endif # SND_PCMCIA
+
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
index 157b0b539f3..989e04abb52 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -151,7 +151,7 @@ static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *
unsigned int i;
int c;
int regCSUER, regRUER;
- unsigned char *image;
+ const unsigned char *image;
unsigned char data;
/* Switch to programmation mode */
@@ -408,7 +408,8 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
int offset = pipe->hw_ptr;
unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
- snd_assert(count % 2 == 0, return);
+ if (snd_BUG_ON(count % 2))
+ return;
vx_setup_pseudo_dma(chip, 0);
if (offset + count > pipe->buffer_bytes) {
int length = pipe->buffer_bytes - offset;
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig
index cacb0b13688..777de2b1717 100644
--- a/sound/ppc/Kconfig
+++ b/sound/ppc/Kconfig
@@ -1,17 +1,17 @@
# ALSA PowerMac drivers
-menu "ALSA PowerMac devices"
- depends on SND!=n && PPC
-
-comment "ALSA PowerMac requires I2C"
- depends on SND && I2C=n
+menuconfig SND_PPC
+ bool "PowerPC sound devices"
+ depends on PPC64 || PPC32
+ default y
+ help
+ Support for sound devices specific to PowerPC architectures.
-comment "ALSA PowerMac requires INPUT"
- depends on SND && INPUT=n
+if SND_PPC
config SND_POWERMAC
tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)"
- depends on SND && I2C && INPUT && PPC_PMAC
+ depends on I2C && INPUT && PPC_PMAC
select SND_PCM
help
Say Y here to include support for the integrated sound device.
@@ -32,14 +32,9 @@ config SND_POWERMAC_AUTO_DRC
Note that you can turn on/off DRC manually even without this
option.
-endmenu
-
-menu "ALSA PowerPC devices"
- depends on SND!=n && ( PPC64 || PPC32 )
-
config SND_PS3
tristate "PS3 Audio support"
- depends on SND && PS3_PS3AV
+ depends on PS3_PS3AV
select SND_PCM
default m
help
@@ -52,4 +47,5 @@ config SND_PS3_DEFAULT_START_DELAY
int "Startup delay time in ms"
depends on SND_PS3
default "2000"
-endmenu
+
+endif # SND_PPC
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 566a6d0daf4..7bd33e6552a 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -319,7 +319,8 @@ static void awacs_amp_set_master(struct awacs_amp *amp, int vol)
static void awacs_amp_free(struct snd_pmac *chip)
{
struct awacs_amp *amp = chip->mixer_data;
- snd_assert(amp, return);
+ if (!amp)
+ return;
kfree(amp);
chip->mixer_data = NULL;
chip->mixer_free = NULL;
@@ -345,8 +346,7 @@ static int snd_pmac_awacs_get_volume_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;
- snd_assert(amp, return -EINVAL);
- snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);
ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);
return 0;
@@ -359,8 +359,6 @@ static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol,
int index = kcontrol->private_value;
int vol[2];
struct awacs_amp *amp = chip->mixer_data;
- snd_assert(amp, return -EINVAL);
- snd_assert(index >= 0 && index <= 1, return -EINVAL);
vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))
| (amp->amp_vol[index][0] & 32);
@@ -375,8 +373,7 @@ static int snd_pmac_awacs_get_switch_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;
- snd_assert(amp, return -EINVAL);
- snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)
? 0 : 1;
ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32)
@@ -391,8 +388,6 @@ static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol,
int index = kcontrol->private_value;
int vol[2];
struct awacs_amp *amp = chip->mixer_data;
- snd_assert(amp, return -EINVAL);
- snd_assert(index >= 0 && index <= 1, return -EINVAL);
vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)
| (amp->amp_vol[index][0] & 31);
@@ -417,8 +412,7 @@ static int snd_pmac_awacs_get_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;
- snd_assert(amp, return -EINVAL);
- snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
ucontrol->value.integer.value[0] = amp->amp_tone[index];
return 0;
}
@@ -430,8 +424,7 @@ static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *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);
+
val = ucontrol->value.integer.value[0];
if (val > 14)
return -EINVAL;
@@ -458,7 +451,7 @@ static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct awacs_amp *amp = chip->mixer_data;
- snd_assert(amp, return -EINVAL);
+
ucontrol->value.integer.value[0] = amp->amp_master;
return 0;
}
@@ -469,7 +462,7 @@ 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);
+
val = ucontrol->value.integer.value[0];
if (val > 99)
return -EINVAL;
@@ -621,6 +614,13 @@ static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
};
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __initdata = {
+ AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
+ AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
+ AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
+ AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
+};
+
static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __initdata = {
AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
@@ -688,7 +688,10 @@ static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = {
static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =
AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __initdata =
+AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
+
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __initdata =
AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
@@ -765,11 +768,12 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip)
#define IS_PM7500 (machine_is_compatible("AAPL,7500"))
#define IS_BEIGE (machine_is_compatible("AAPL,Gossamer"))
-#define IS_IMAC (machine_is_compatible("PowerMac2,1") \
- || machine_is_compatible("PowerMac2,2") \
+#define IS_IMAC1 (machine_is_compatible("PowerMac2,1"))
+#define IS_IMAC2 (machine_is_compatible("PowerMac2,2") \
|| machine_is_compatible("PowerMac4,1"))
+#define IS_G4AGP (machine_is_compatible("PowerMac3,1"))
-static int imac;
+static int imac1, imac2;
#ifdef PMAC_SUPPORT_AUTOMUTE
/*
@@ -815,13 +819,18 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
{
int reg = chip->awacs_reg[1]
| (MASK_HDMUTE | MASK_SPKMUTE);
- if (imac) {
+ if (imac1) {
+ reg &= ~MASK_SPKMUTE;
+ reg |= MASK_PAROUT1;
+ } else if (imac2) {
reg &= ~MASK_SPKMUTE;
reg &= ~MASK_PAROUT1;
}
if (snd_pmac_awacs_detect_headphone(chip))
reg &= ~MASK_HDMUTE;
- else if (imac)
+ else if (imac1)
+ reg &= ~MASK_PAROUT1;
+ else if (imac2)
reg |= MASK_PAROUT1;
else
reg &= ~MASK_SPKMUTE;
@@ -850,9 +859,13 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
{
int pm7500 = IS_PM7500;
int beige = IS_BEIGE;
+ int g4agp = IS_G4AGP;
+ int imac;
int err, vol;
- imac = IS_IMAC;
+ imac1 = IS_IMAC1;
+ imac2 = IS_IMAC2;
+ imac = imac1 || imac2;
/* looks like MASK_GAINLINE triggers something, so we set here
* as start-up
*/
@@ -939,7 +952,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
snd_pmac_awacs_mixers);
if (err < 0)
return err;
- if (beige)
+ if (beige || g4agp)
;
else if (chip->model == PMAC_SCREAMER)
err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
@@ -961,13 +974,17 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_screamer_mixers_imac),
snd_pmac_screamer_mixers_imac);
+ else if (g4agp)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp),
+ snd_pmac_screamer_mixers_g4agp);
else
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_awacs_mixers_pmac),
snd_pmac_awacs_mixers_pmac);
if (err < 0)
return err;
- chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac)
+ chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp)
? &snd_pmac_awacs_master_sw_imac
: &snd_pmac_awacs_master_sw, chip);
err = snd_ctl_add(chip->card, chip->master_sw_ctl);
@@ -1004,15 +1021,17 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
snd_pmac_awacs_speaker_vol);
if (err < 0)
return err;
- chip->speaker_sw_ctl = snd_ctl_new1(imac
- ? &snd_pmac_awacs_speaker_sw_imac
+ chip->speaker_sw_ctl = snd_ctl_new1(imac1
+ ? &snd_pmac_awacs_speaker_sw_imac1
+ : imac2
+ ? &snd_pmac_awacs_speaker_sw_imac2
: &snd_pmac_awacs_speaker_sw, chip);
err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
if (err < 0)
return err;
}
- if (beige)
+ if (beige || g4agp)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),
snd_pmac_screamer_mic_boost_beige);
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index baa2a723737..89f5c328acf 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -185,7 +185,8 @@ static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
- snd_assert(chip->beep, return -ENXIO);
+ if (snd_BUG_ON(!chip->beep))
+ return -ENXIO;
ucontrol->value.integer.value[0] = chip->beep->volume;
return 0;
}
@@ -195,7 +196,8 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
unsigned int oval, nval;
- snd_assert(chip->beep, return -ENXIO);
+ if (snd_BUG_ON(!chip->beep))
+ return -ENXIO;
oval = chip->beep->volume;
nval = ucontrol->value.integer.value[0];
if (nval > 100)
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index ca9452901a5..8a5b2903193 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -249,9 +249,7 @@ int __init snd_pmac_daca_init(struct snd_pmac *chip)
int i, err;
struct pmac_daca *mix;
-#ifdef CONFIG_KMOD
request_module("i2c-powermac");
-#endif /* CONFIG_KMOD */
mix = kzalloc(sizeof(*mix), GFP_KERNEL);
if (! mix)
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 3f8d7164cef..f746e15b848 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -263,7 +263,7 @@ static int tumbler_get_master_volume(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix = chip->mixer_data;
- snd_assert(mix, return -ENODEV);
+
ucontrol->value.integer.value[0] = mix->master_vol[0];
ucontrol->value.integer.value[1] = mix->master_vol[1];
return 0;
@@ -277,7 +277,6 @@ static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol,
unsigned int vol[2];
int change;
- snd_assert(mix, return -ENODEV);
vol[0] = ucontrol->value.integer.value[0];
vol[1] = ucontrol->value.integer.value[1];
if (vol[0] >= ARRAY_SIZE(master_volume_table) ||
@@ -299,7 +298,7 @@ static int tumbler_get_master_switch(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix = chip->mixer_data;
- snd_assert(mix, return -ENODEV);
+
ucontrol->value.integer.value[0] = mix->master_switch[0];
ucontrol->value.integer.value[1] = mix->master_switch[1];
return 0;
@@ -312,7 +311,6 @@ static int tumbler_put_master_switch(struct snd_kcontrol *kcontrol,
struct pmac_tumbler *mix = chip->mixer_data;
int change;
- snd_assert(mix, return -ENODEV);
change = mix->master_switch[0] != ucontrol->value.integer.value[0] ||
mix->master_switch[1] != ucontrol->value.integer.value[1];
if (change) {
@@ -807,7 +805,6 @@ static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix = chip->mixer_data;
- snd_assert(mix, return -ENODEV);
ucontrol->value.enumerated.item[0] = mix->capture_source;
return 0;
}
@@ -819,7 +816,6 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
struct pmac_tumbler *mix = chip->mixer_data;
int change;
- snd_assert(mix, return -ENODEV);
change = ucontrol->value.enumerated.item[0] != mix->capture_source;
if (change) {
mix->capture_source = !!ucontrol->value.enumerated.item[0];
@@ -978,7 +974,8 @@ static void device_change_handler(struct work_struct *work)
return;
mix = chip->mixer_data;
- snd_assert(mix, return);
+ if (snd_BUG_ON(!mix))
+ return;
headphone = tumbler_detect_headphone(chip);
lineout = tumbler_detect_lineout(chip);
@@ -1033,7 +1030,8 @@ static void tumbler_update_automute(struct snd_pmac *chip, int do_notify)
if (chip->auto_mute) {
struct pmac_tumbler *mix;
mix = chip->mixer_data;
- snd_assert(mix, return);
+ if (snd_BUG_ON(!mix))
+ return;
mix->auto_mute_notify = do_notify;
schedule_work(&device_change);
}
@@ -1227,8 +1225,6 @@ static void tumbler_resume(struct snd_pmac *chip)
{
struct pmac_tumbler *mix = chip->mixer_data;
- snd_assert(mix, return);
-
mix->acs &= ~1;
mix->master_switch[0] = mix->save_master_switch[0];
mix->master_switch[1] = mix->save_master_switch[1];
@@ -1275,7 +1271,6 @@ static int __init tumbler_init(struct snd_pmac *chip)
{
int irq;
struct pmac_tumbler *mix = chip->mixer_data;
- snd_assert(mix, return -EINVAL);
if (tumbler_find_device("audio-hw-reset",
"platform-do-hw-reset",
@@ -1350,9 +1345,7 @@ int __init snd_pmac_tumbler_init(struct snd_pmac *chip)
struct device_node *tas_node, *np;
char *chipname;
-#ifdef CONFIG_KMOD
request_module("i2c-powermac");
-#endif /* CONFIG_KMOD */
mix = kzalloc(sizeof(*mix), GFP_KERNEL);
if (! mix)
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index b7e08ef22a9..cfc14398580 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -1,14 +1,22 @@
# ALSA SH drivers
-menu "SUPERH devices"
- depends on SND!=n && SUPERH
+menuconfig SND_SUPERH
+ bool "SUPERH sound devices"
+ depends on SUPERH
+ default y
+ help
+ Support for sound devices specific to SUPERH architectures.
+ Drivers that are implemented on ASoC can be found in
+ "ALSA for SoC audio support" section.
+
+if SND_SUPERH
config SND_AICA
tristate "Dreamcast Yamaha AICA sound"
- depends on SH_DREAMCAST && SND
+ depends on SH_DREAMCAST
select SND_PCM
help
ALSA Sound driver for the SEGA Dreamcast console.
-endmenu
+endif # SND_SUPERH
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 9ca11332614..7c920f3e7fe 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -42,7 +42,7 @@
#include <sound/info.h>
#include <asm/io.h>
#include <asm/dma.h>
-#include <asm/dreamcast/sysasic.h>
+#include <mach/sysasic.h>
#include "aica.h"
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
@@ -106,7 +106,8 @@ static void spu_memset(u32 toi, u32 what, int length)
{
int i;
unsigned long flags;
- snd_assert(length % 4 == 0, return);
+ if (snd_BUG_ON(length % 4))
+ return;
for (i = 0; i < length; i++) {
if (!(i % 8))
spu_write_wait();
@@ -589,7 +590,7 @@ static int __devinit add_aicamixer_controls(struct snd_card_aica
return 0;
}
-static int snd_aica_remove(struct platform_device *devptr)
+static int __devexit snd_aica_remove(struct platform_device *devptr)
{
struct snd_card_aica *dreamcastcard;
dreamcastcard = platform_get_drvdata(devptr);
@@ -601,7 +602,7 @@ static int snd_aica_remove(struct platform_device *devptr)
return 0;
}
-static int __init snd_aica_probe(struct platform_device *devptr)
+static int __devinit snd_aica_probe(struct platform_device *devptr)
{
int err;
struct snd_card_aica *dreamcastcard;
@@ -650,7 +651,7 @@ static int __init snd_aica_probe(struct platform_device *devptr)
static struct platform_driver snd_aica_driver = {
.probe = snd_aica_probe,
- .remove = snd_aica_remove,
+ .remove = __devexit_p(snd_aica_remove),
.driver = {
.name = SND_AICA_DRIVER},
};
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 18f28ac4bfe..4dfda6674be 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -2,16 +2,10 @@
# SoC audio configuration
#
-menu "System on Chip audio support"
- depends on SND!=n
-
-config SND_SOC_AC97_BUS
- bool
-
-config SND_SOC
+menuconfig SND_SOC
tristate "ALSA for SoC audio support"
- depends on SND
select SND_PCM
+ select AC97_BUS if SND_SOC_AC97_BUS
---help---
If you want ASoC support, you should say Y here and also to the
@@ -23,16 +17,25 @@ config SND_SOC
This ASoC audio support can also be built as a module. If so, the module
will be called snd-soc-core.
+if SND_SOC
+
+config SND_SOC_AC97_BUS
+ bool
+
# All the supported Soc's
+source "sound/soc/at32/Kconfig"
source "sound/soc/at91/Kconfig"
+source "sound/soc/au1x/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/fsl/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/omap/Kconfig"
+source "sound/soc/blackfin/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
-endmenu
+endif # SND_SOC
+
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 782db212710..d849349f2c6 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,5 @@
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/ fsl/ davinci/ omap/
+obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
+obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
new file mode 100644
index 00000000000..b0765e86c08
--- /dev/null
+++ b/sound/soc/at32/Kconfig
@@ -0,0 +1,34 @@
+config SND_AT32_SOC
+ tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
+ depends on AVR32 && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the AT32 SSC interface. You will also need to
+ to select the audio interfaces to support below.
+
+
+config SND_AT32_SOC_SSC
+ tristate
+
+
+
+config SND_AT32_SOC_PLAYPAQ
+ tristate "SoC Audio support for PlayPaq with WM8510"
+ depends on SND_AT32_SOC && BOARD_PLAYPAQ
+ select SND_AT32_SOC_SSC
+ select SND_SOC_WM8510
+ help
+ Say Y or M here if you want to add support for SoC audio
+ on the LRS PlayPaq.
+
+
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+ bool "Run CODEC on PlayPaq in slave mode"
+ depends on SND_AT32_SOC_PLAYPAQ
+ default n
+ help
+ Say Y if you want to run with the AT32 SSC generating the BCLK
+ and FRAME signals on the PlayPaq. Unless you want to play
+ with the AT32 as the SSC master, you probably want to say N here,
+ as this will give you better sound quality.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
new file mode 100644
index 00000000000..c03e55ecece
--- /dev/null
+++ b/sound/soc/at32/Makefile
@@ -0,0 +1,11 @@
+# AT32 Platform Support
+snd-soc-at32-objs := at32-pcm.o
+snd-soc-at32-ssc-objs := at32-ssc.o
+
+obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
+obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c
new file mode 100644
index 00000000000..c83584f989a
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.c
@@ -0,0 +1,492 @@
+/* sound/soc/at32/at32-pcm.c
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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.
+ *
+ * Note that this is basically a port of the sound/soc/at91-pcm.c to
+ * the AVR32 kernel. Thanks to Frank Mandarino for that code.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ * them against real values for AT32
+ */
+static const struct snd_pcm_hardware at32_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE),
+
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192, /* 512 frames * 16 bytes / frame */
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 32 * 1024,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct at32_runtime_data {
+ struct at32_pcm_dma_params *params;
+ dma_addr_t dma_buffer; /* physical address of DMA buffer */
+ dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
+ size_t period_size;
+
+ dma_addr_t period_ptr; /* physical address of next period */
+ int periods; /* period index of period_ptr */
+
+ /* Save PDC registers (for power management) */
+ u32 pdc_xpr_save;
+ u32 pdc_xcr_save;
+ u32 pdc_xnpr_save;
+ u32 pdc_xncr_save;
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
+ size_t size = at32_pcm_hardware.buffer_bytes_max;
+
+ dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dmabuf->dev.dev = pcm->card->dev;
+ dmabuf->private_data = NULL;
+ dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &dmabuf->addr, GFP_KERNEL);
+ pr_debug("at32_pcm: preallocate_dma_buffer: "
+ "area=%p, addr=%p, size=%ld\n",
+ (void *)dmabuf->area, (void *)dmabuf->addr, size);
+
+ if (!dmabuf->area)
+ return -ENOMEM;
+
+ dmabuf->bytes = size;
+ return 0;
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ struct at32_runtime_data *prtd = rtd->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+ static int count;
+
+ count++;
+ if (ssc_sr & params->mask->ssc_endbuf) {
+ pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "underrun" : "overrun", params->name, ssc_sr, count);
+
+ /* re-start the PDC */
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end)
+ prtd->period_ptr = prtd->dma_buffer;
+
+
+ ssc_writex(params->ssc->regs, params->pdc->xpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_enable);
+ }
+
+
+ if (ssc_sr & params->mask->ssc_endx) {
+ /* Load the PDC next pointer and counter registers */
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end)
+ prtd->period_ptr = prtd->dma_buffer;
+ ssc_writex(params->ssc->regs, params->pdc->xnpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+ }
+
+
+ snd_pcm_period_elapsed(substream);
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at32_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* this may get called several times by oss emulation
+ * with different params
+ */
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->params = rtd->dai->cpu_dai->dma_data;
+ prtd->params->dma_intr_handler = at32_pcm_dma_irq;
+
+ prtd->dma_buffer = runtime->dma_addr;
+ prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+ prtd->period_size = params_period_bytes(params);
+
+ pr_debug("hw_params: DMA for %s initialized "
+ "(dma_bytes=%ld, period_size=%ld)\n",
+ prtd->params->name, runtime->dma_bytes, prtd->period_size);
+
+ return 0;
+}
+
+
+
+static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct at32_runtime_data *prtd = substream->runtime->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+
+ if (params != NULL) {
+ ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+ params->mask->pdc_disable);
+ prtd->params->dma_intr_handler = NULL;
+ }
+
+ return 0;
+}
+
+
+
+static int at32_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct at32_runtime_data *prtd = substream->runtime->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+
+ ssc_writex(params->ssc->regs, SSC_IDR,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+
+ return 0;
+}
+
+
+static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ struct at32_runtime_data *prtd = rtd->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+ int ret = 0;
+
+ pr_debug("at32_pcm_trigger: buffer_size = %ld, "
+ "dma_area = %p, dma_bytes = %ld\n",
+ rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ prtd->period_ptr += prtd->period_size;
+ ssc_writex(params->ssc->regs, params->pdc->xnpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ pr_debug("trigger: period_ptr=%lx, xpr=%x, "
+ "xcr=%d, xnpr=%x, xncr=%d\n",
+ (unsigned long)prtd->period_ptr,
+ ssc_readx(params->ssc->regs, params->pdc->xpr),
+ ssc_readx(params->ssc->regs, params->pdc->xcr),
+ ssc_readx(params->ssc->regs, params->pdc->xnpr),
+ ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+ ssc_writex(params->ssc->regs, SSC_IER,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+ ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+ params->mask->pdc_enable);
+
+ pr_debug("sr=%x, imr=%x\n",
+ ssc_readx(params->ssc->regs, SSC_SR),
+ ssc_readx(params->ssc->regs, SSC_IER));
+ break; /* SNDRV_PCM_TRIGGER_START */
+
+
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ break;
+
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_enable);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+
+
+static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at32_runtime_data *prtd = runtime->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+ dma_addr_t ptr;
+ snd_pcm_uframes_t x;
+
+ ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+ x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+
+ return x;
+}
+
+
+
+static int at32_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at32_runtime_data *prtd;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
+
+ /* ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ runtime->private_data = prtd;
+
+
+out:
+ return ret;
+}
+
+
+
+static int at32_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct at32_runtime_data *prtd = substream->runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+
+static int at32_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+
+
+static struct snd_pcm_ops at32_pcm_ops = {
+ .open = at32_pcm_open,
+ .close = at32_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = at32_pcm_hw_params,
+ .hw_free = at32_pcm_hw_free,
+ .prepare = at32_pcm_prepare,
+ .trigger = at32_pcm_trigger,
+ .pointer = at32_pcm_pointer,
+ .mmap = at32_pcm_mmap,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 at32_pcm_dmamask = 0xffffffff;
+
+static int at32_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &at32_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = at32_pcm_preallocate_dma_buffer(
+ pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
+ ret = at32_pcm_preallocate_dma_buffer(
+ pcm, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+
+out:
+ return ret;
+}
+
+
+
+static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (substream == NULL)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_pcm_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct at32_runtime_data *prtd;
+ struct at32_pcm_dma_params *params;
+
+ if (runtime == NULL)
+ return 0;
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* Disable the PDC and save the PDC registers */
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+
+ prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+ prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+ prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+ prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+ return 0;
+}
+
+
+
+static int at32_pcm_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct at32_runtime_data *prtd;
+ struct at32_pcm_dma_params *params;
+
+ if (runtime == NULL)
+ return 0;
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* Restore the PDC registers and enable the PDC */
+ ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);
+ return 0;
+}
+#else /* CONFIG_PM */
+# define at32_pcm_suspend NULL
+# define at32_pcm_resume NULL
+#endif /* CONFIG_PM */
+
+
+
+struct snd_soc_platform at32_soc_platform = {
+ .name = "at32-audio",
+ .pcm_ops = &at32_pcm_ops,
+ .pcm_new = at32_pcm_new,
+ .pcm_free = at32_pcm_free_dma_buffers,
+ .suspend = at32_pcm_suspend,
+ .resume = at32_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(at32_soc_platform);
+
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("Atmel AT32 PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
new file mode 100644
index 00000000000..2a52430417d
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.h
@@ -0,0 +1,79 @@
+/* sound/soc/at32/at32-pcm.h
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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 __SOUND_SOC_AT32_AT32_PCM_H
+#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
+
+#include <linux/atmel-ssc.h>
+
+
+/*
+ * Registers and status bits that are required by the PCM driver
+ * TODO: Is ptcr really used?
+ */
+struct at32_pdc_regs {
+ u32 xpr; /* PDC RX/TX pointer */
+ u32 xcr; /* PDC RX/TX counter */
+ u32 xnpr; /* PDC next RX/TX pointer */
+ u32 xncr; /* PDC next RX/TX counter */
+ u32 ptcr; /* PDC transfer control */
+};
+
+
+
+/*
+ * SSC mask info
+ */
+struct at32_ssc_mask {
+ u32 ssc_enable; /* SSC RX/TX enable */
+ u32 ssc_disable; /* SSC RX/TX disable */
+ u32 ssc_endx; /* SSC ENDTX or ENDRX */
+ u32 ssc_endbuf; /* SSC TXBUFF or RXBUFF */
+ u32 pdc_enable; /* PDC RX/TX enable */
+ u32 pdc_disable; /* PDC RX/TX disable */
+};
+
+
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation. All fields except dma_intr_handler() are initialized
+ * by the interface. The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct at32_pcm_dma_params {
+ char *name; /* stream identifier */
+ int pdc_xfer_size; /* PDC counter increment in bytes */
+ struct ssc_device *ssc; /* SSC device for stream */
+ struct at32_pdc_regs *pdc; /* PDC register info */
+ struct at32_ssc_mask *mask; /* SSC mask info */
+ struct snd_pcm_substream *substream;
+ void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
+};
+
+
+
+/*
+ * The AT32 ASoC platform driver
+ */
+extern struct snd_soc_platform at32_soc_platform;
+
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
+
+#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
new file mode 100644
index 00000000000..4ef6492c902
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.c
@@ -0,0 +1,849 @@
+/* sound/soc/at32/at32-ssc.c
+ * ASoC platform driver for AT32 using SSC as DAI
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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.
+ *
+ * Note that this is basically a port of the sound/soc/at91-ssc.c to
+ * the AVR32 kernel. Thanks to Frank Mandarino for that code.
+ */
+
+/* #define DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Constants
+\*-------------------------------------------------------------------------*/
+#define NUM_SSC_DEVICES 3
+
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED 0
+#define SSC_DIR_MASK_PLAYBACK 1
+#define SSC_DIR_MASK_CAPTURE 2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>. These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS 0
+#define SSC_START_TX_RX 1
+#define SSC_START_LOW_RF 2
+#define SSC_START_HIGH_RF 3
+#define SSC_START_FALLING_RF 4
+#define SSC_START_RISING_RF 5
+#define SSC_START_LEVEL_RF 6
+#define SSC_START_EDGE_RF 7
+#define SSS_START_COMPARE_0 8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING 0
+#define SSC_CKI_RISING 1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE 0
+#define SSC_CKO_CONTINUOUS 1
+#define SSC_CKO_TRANSFER 2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV 0
+#define SSC_CKS_CLOCK 1
+#define SSC_CKS_PIN 2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE 0
+#define SSC_FSEDGE_NEGATIVE 1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE 0
+#define SSC_FSOS_NEGATIVE 1
+#define SSC_FSOS_POSITIVE 2
+#define SSC_FSOS_LOW 3
+#define SSC_FSOS_HIGH 4
+#define SSC_FSOS_TOGGLE 5
+
+#define START_DELAY 1
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Module data
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC PDC registered required by the PCM DMA engine
+ */
+static struct at32_pdc_regs pdc_tx_reg = {
+ .xpr = SSC_PDC_TPR,
+ .xcr = SSC_PDC_TCR,
+ .xnpr = SSC_PDC_TNPR,
+ .xncr = SSC_PDC_TNCR,
+};
+
+
+
+static struct at32_pdc_regs pdc_rx_reg = {
+ .xpr = SSC_PDC_RPR,
+ .xcr = SSC_PDC_RCR,
+ .xnpr = SSC_PDC_RNPR,
+ .xncr = SSC_PDC_RNCR,
+};
+
+
+
+/*
+ * SSC and PDC status bits for transmit and receive
+ */
+static struct at32_ssc_mask ssc_tx_mask = {
+ .ssc_enable = SSC_BIT(CR_TXEN),
+ .ssc_disable = SSC_BIT(CR_TXDIS),
+ .ssc_endx = SSC_BIT(SR_ENDTX),
+ .ssc_endbuf = SSC_BIT(SR_TXBUFE),
+ .pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
+ .pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
+};
+
+
+
+static struct at32_ssc_mask ssc_rx_mask = {
+ .ssc_enable = SSC_BIT(CR_RXEN),
+ .ssc_disable = SSC_BIT(CR_RXDIS),
+ .ssc_endx = SSC_BIT(SR_ENDRX),
+ .ssc_endbuf = SSC_BIT(SR_RXBUFF),
+ .pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
+ .pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
+};
+
+
+
+/*
+ * DMA parameters for each SSC
+ */
+static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+ {
+ {
+ .name = "SSC0 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC0 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ },
+ },
+ {
+ {
+ .name = "SSC1 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC1 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ },
+ },
+ {
+ {
+ .name = "SSC2 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC2 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ },
+ },
+};
+
+
+
+static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+ {
+ .name = "ssc0",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+ {
+ .name = "ssc1",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+ {
+ .name = "ssc2",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+};
+
+
+
+
+/*-------------------------------------------------------------------------*\
+ * ISR
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC interrupt handler. Passes PDC interrupts to the DMA interrupt
+ * handler in the PCM driver.
+ */
+static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
+{
+ struct at32_ssc_info *ssc_p = dev_id;
+ struct at32_pcm_dma_params *dma_params;
+ u32 ssc_sr;
+ u32 ssc_substream_mask;
+ int i;
+
+ ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
+ ssc_readl(ssc_p->ssc->regs, IMR));
+
+ /*
+ * Loop through substreams attached to this SSC. If a DMA-related
+ * interrupt occured on that substream, call the DMA interrupt
+ * handler function, if one has been registered in the dma_param
+ * structure by the PCM driver.
+ */
+ for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+ dma_params = ssc_p->dma_params[i];
+
+ if ((dma_params != NULL) &&
+ (dma_params->dma_intr_handler != NULL)) {
+ ssc_substream_mask = (dma_params->mask->ssc_endx |
+ dma_params->mask->ssc_endbuf);
+ if (ssc_sr & ssc_substream_mask) {
+ dma_params->dma_intr_handler(ssc_sr,
+ dma_params->
+ substream);
+ }
+ }
+ }
+
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup. Only that one substream allowed in each direction.
+ */
+static int at32_ssc_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ int dir_mask;
+
+ dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
+
+ spin_lock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask) {
+ spin_unlock_irq(&ssc_p->lock);
+ return -EBUSY;
+ }
+ ssc_p->dir_mask |= dir_mask;
+ spin_unlock_irq(&ssc_p->lock);
+
+ return 0;
+}
+
+
+
+/*
+ * Shutdown. Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct at32_pcm_dma_params *dma_params;
+ int dir_mask;
+
+ dma_params = ssc_p->dma_params[substream->stream];
+
+ if (dma_params != NULL) {
+ ssc_writel(dma_params->ssc->regs, CR,
+ dma_params->mask->ssc_disable);
+ pr_debug("%s disabled SSC_SR=0x%08x\n",
+ (substream->stream ? "receiver" : "transmit"),
+ ssc_readl(ssc_p->ssc->regs, SR));
+
+ dma_params->ssc = NULL;
+ dma_params->substream = NULL;
+ ssc_p->dma_params[substream->stream] = NULL;
+ }
+
+
+ dir_mask = 1 << substream->stream;
+ spin_lock_irq(&ssc_p->lock);
+ ssc_p->dir_mask &= ~dir_mask;
+ if (!ssc_p->dir_mask) {
+ /* Shutdown the SSC clock */
+ pr_debug("at32-ssc: Stopping user %d clock\n",
+ ssc_p->ssc->user);
+ clk_disable(ssc_p->ssc->clk);
+
+ if (ssc_p->initialized) {
+ free_irq(ssc_p->ssc->irq, ssc_p);
+ ssc_p->initialized = 0;
+ }
+
+ /* Reset the SSC */
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+ /* clear the SSC dividers */
+ ssc_p->cmr_div = 0;
+ ssc_p->tcmr_period = 0;
+ ssc_p->rcmr_period = 0;
+ }
+ spin_unlock_irq(&ssc_p->lock);
+}
+
+
+
+/*
+ * Set the SSC system clock rate
+ */
+static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ /* TODO: What the heck do I do here? */
+ return 0;
+}
+
+
+
+/*
+ * Record DAI format for use by hw_params()
+ */
+static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ ssc_p->daifmt = fmt;
+ return 0;
+}
+
+
+
+/*
+ * Record SSC clock dividers for use in hw_params()
+ */
+static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ switch (div_id) {
+ case AT32_SSC_CMR_DIV:
+ /*
+ * The same master clock divider is used for both
+ * transmit and receive, so if a value has already
+ * been set, it must match this value
+ */
+ if (ssc_p->cmr_div == 0)
+ ssc_p->cmr_div = div;
+ else if (div != ssc_p->cmr_div)
+ return -EBUSY;
+ break;
+
+ case AT32_SSC_TCMR_PERIOD:
+ ssc_p->tcmr_period = div;
+ break;
+
+ case AT32_SSC_RCMR_PERIOD:
+ ssc_p->rcmr_period = div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Configure the SSC
+ */
+static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int id = rtd->dai->cpu_dai->id;
+ struct at32_ssc_info *ssc_p = &ssc_info[id];
+ struct at32_pcm_dma_params *dma_params;
+ int channels, bits;
+ u32 tfmr, rfmr, tcmr, rcmr;
+ int start_event;
+ int ret;
+
+
+ /*
+ * Currently, there is only one set of dma_params for each direction.
+ * If more are added, this code will have to be changed to select
+ * the proper set
+ */
+ dma_params = &ssc_dma_params[id][substream->stream];
+ dma_params->ssc = ssc_p->ssc;
+ dma_params->substream = substream;
+
+ ssc_p->dma_params[substream->stream] = dma_params;
+
+
+ /*
+ * The cpu_dai->dma_data field is only used to communicate the
+ * appropriate DMA parameters to the PCM driver's hw_params()
+ * function. It should not be used for other purposes as it
+ * is common to all substreams.
+ */
+ rtd->dai->cpu_dai->dma_data = dma_params;
+
+ channels = params_channels(params);
+
+
+ /*
+ * Determine sample size in bits and the PDC increment
+ */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ bits = 8;
+ dma_params->pdc_xfer_size = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16:
+ bits = 16;
+ dma_params->pdc_xfer_size = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24:
+ bits = 24;
+ dma_params->pdc_xfer_size = 4;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32:
+ bits = 32;
+ dma_params->pdc_xfer_size = 4;
+ break;
+
+ default:
+ pr_warning("at32-ssc: Unsupported PCM format %d",
+ params_format(params));
+ return -EINVAL;
+ }
+ pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
+ bits, dma_params->pdc_xfer_size, channels);
+
+
+ /*
+ * The SSC only supports up to 16-bit samples in I2S format, due
+ * to the size of the Frame Mode Register FSLEN field.
+ */
+ if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ if (bits > 16) {
+ pr_warning("at32-ssc: "
+ "sample size %d is too large for I2S\n",
+ bits);
+ return -EINVAL;
+ }
+
+
+ /*
+ * Compute the SSC register settings
+ */
+ switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * I2S format, SSC provides BCLK and LRS clocks.
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output on the SSC TK line
+ */
+ pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
+ rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+ SSC_BF(RCMR_STTDLY, START_DELAY) |
+ SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
+ SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+ rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
+ SSC_BF(RFMR_FSLEN, bits - 1) |
+ SSC_BF(RFMR_DATNB, channels - 1) |
+ SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+ tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+ SSC_BF(TCMR_STTDLY, START_DELAY) |
+ SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
+ SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+ SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+ SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+ tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
+ SSC_BF(TFMR_FSLEN, bits - 1) |
+ SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
+ SSC_BF(TFMR_DATLEN, bits - 1));
+ break;
+
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ /*
+ * I2S format, CODEC supplies BCLK and LRC clock.
+ *
+ * The SSC transmit clock is obtained from the BCLK signal
+ * on the TK line, and the SSC receive clock is generated from
+ * the transmit clock.
+ *
+ * For single channel data, one sample is transferred on the
+ * falling edge of the LRC clock. For two channel data, one
+ * sample is transferred on both edges of the LRC clock.
+ */
+ pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
+ start_event = ((channels == 1) ?
+ SSC_START_FALLING_RF : SSC_START_EDGE_RF);
+
+ rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
+ SSC_BF(RCMR_START, start_event) |
+ SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
+
+ rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
+ SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+ tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
+ SSC_BF(TCMR_START, start_event) |
+ SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+ SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(TCMR_CKS, SSC_CKS_PIN));
+
+ tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
+ SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+ break;
+
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output on the SSC TK line
+ */
+ pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
+ rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+ SSC_BF(RCMR_STTDLY, 1) |
+ SSC_BF(RCMR_START, SSC_START_RISING_RF) |
+ SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+ rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
+ SSC_BF(RFMR_DATNB, channels - 1) |
+ SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+ tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+ SSC_BF(TCMR_STTDLY, 1) |
+ SSC_BF(TCMR_START, SSC_START_RISING_RF) |
+ SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+ SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+ tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
+ SSC_BF(TFMR_DATNB, channels - 1) |
+ SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+ break;
+
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ default:
+ pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
+ ssc_p->daifmt);
+ return -EINVAL;
+ break;
+ }
+ pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+ rcmr, rfmr, tcmr, tfmr);
+
+
+ if (!ssc_p->initialized) {
+ /* enable peripheral clock */
+ pr_debug("at32-ssc: Starting clock\n");
+ clk_enable(ssc_p->ssc->clk);
+
+ /* Reset the SSC and its PDC registers */
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+ ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+ ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+ ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
+ ssc_p->name, ssc_p);
+ if (ret < 0) {
+ pr_warning("at32-ssc: request irq failed (%d)\n", ret);
+ pr_debug("at32-ssc: Stopping clock\n");
+ clk_disable(ssc_p->ssc->clk);
+ return ret;
+ }
+
+ ssc_p->initialized = 1;
+ }
+
+ /* Set SSC clock mode register */
+ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+ /* set receive clock mode and format */
+ ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+ ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+ /* set transmit clock mode and format */
+ ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+ ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+ pr_debug("at32-ssc: SSC initialized\n");
+ return 0;
+}
+
+
+
+static int at32_ssc_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct at32_pcm_dma_params *dma_params;
+
+ dma_params = ssc_p->dma_params[substream->stream];
+
+ ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+ return 0;
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_ssc_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* Save the status register before disabling transmit and receive */
+ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+ /* Save the current interrupt mask, then disable unmasked interrupts */
+ ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+ ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+ ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+ ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+ ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+ ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+ ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+ return 0;
+}
+
+
+
+static int at32_ssc_resume(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p;
+ u32 cr;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* restore SSC register settings */
+ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+ ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+ ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+ ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+ /* re-enable interrupts */
+ ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+ /* Re-enable recieve and transmit as appropriate */
+ cr = 0;
+ cr |=
+ (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+ cr |=
+ (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+ ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+ return 0;
+}
+#else /* CONFIG_PM */
+# define at32_ssc_suspend NULL
+# define at32_ssc_resume NULL
+#endif /* CONFIG_PM */
+
+
+#define AT32_SSC_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+
+#define AT32_SSC_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16 | \
+ SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
+
+
+struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
+ {
+ .name = "at32-ssc0",
+ .id = 0,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = at32_ssc_suspend,
+ .resume = at32_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .ops = {
+ .startup = at32_ssc_startup,
+ .shutdown = at32_ssc_shutdown,
+ .prepare = at32_ssc_prepare,
+ .hw_params = at32_ssc_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = at32_ssc_set_dai_sysclk,
+ .set_fmt = at32_ssc_set_dai_fmt,
+ .set_clkdiv = at32_ssc_set_dai_clkdiv,
+ },
+ .private_data = &ssc_info[0],
+ },
+ {
+ .name = "at32-ssc1",
+ .id = 1,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = at32_ssc_suspend,
+ .resume = at32_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .ops = {
+ .startup = at32_ssc_startup,
+ .shutdown = at32_ssc_shutdown,
+ .prepare = at32_ssc_prepare,
+ .hw_params = at32_ssc_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = at32_ssc_set_dai_sysclk,
+ .set_fmt = at32_ssc_set_dai_fmt,
+ .set_clkdiv = at32_ssc_set_dai_clkdiv,
+ },
+ .private_data = &ssc_info[1],
+ },
+ {
+ .name = "at32-ssc2",
+ .id = 2,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = at32_ssc_suspend,
+ .resume = at32_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .ops = {
+ .startup = at32_ssc_startup,
+ .shutdown = at32_ssc_shutdown,
+ .prepare = at32_ssc_prepare,
+ .hw_params = at32_ssc_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = at32_ssc_set_dai_sysclk,
+ .set_fmt = at32_ssc_set_dai_fmt,
+ .set_clkdiv = at32_ssc_set_dai_clkdiv,
+ },
+ .private_data = &ssc_info[2],
+ },
+};
+EXPORT_SYMBOL_GPL(at32_ssc_dai);
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
new file mode 100644
index 00000000000..3c052dbbe46
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.h
@@ -0,0 +1,59 @@
+/* sound/soc/at32/at32-ssc.h
+ * ASoC SSC interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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 __SOUND_SOC_AT32_AT32_SSC_H
+#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "at32-pcm.h"
+
+
+
+struct at32_ssc_state {
+ u32 ssc_cmr;
+ u32 ssc_rcmr;
+ u32 ssc_rfmr;
+ u32 ssc_tcmr;
+ u32 ssc_tfmr;
+ u32 ssc_sr;
+ u32 ssc_imr;
+};
+
+
+
+struct at32_ssc_info {
+ char *name;
+ struct ssc_device *ssc;
+ spinlock_t lock; /* lock for dir_mask */
+ unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
+ unsigned short initialized; /* true if SSC has been initialized */
+ unsigned short daifmt;
+ unsigned short cmr_div;
+ unsigned short tcmr_period;
+ unsigned short rcmr_period;
+ struct at32_pcm_dma_params *dma_params[2];
+ struct at32_ssc_state ssc_state;
+};
+
+
+/* SSC divider ids */
+#define AT32_SSC_CMR_DIV 0 /* MCK divider for BCLK */
+#define AT32_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
+#define AT32_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
+
+
+extern struct snd_soc_dai at32_ssc_dai[];
+
+
+
+#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c
new file mode 100644
index 00000000000..98a2d5826a8
--- /dev/null
+++ b/sound/soc/at32/playpaq_wm8510.c
@@ -0,0 +1,519 @@
+/* sound/soc/at32/playpaq_wm8510.c
+ * ASoC machine driver for PlayPaq using WM8510 codec
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
+ *
+ * NOTE: If you don't have the AT32 enhanced portmux configured (which
+ * isn't currently in the mainline or Atmel patched kernel), you will
+ * need to set the MCLK pin (PA30) to peripheral A in your board initialization
+ * code. Something like:
+ * at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.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 <mach/at32ap700x.h>
+#include <mach/portmux.h>
+
+#include "../codecs/wm8510.h"
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+/*-------------------------------------------------------------------------*\
+ * constants
+\*-------------------------------------------------------------------------*/
+#define MCLK_PIN GPIO_PIN_PA(30)
+#define MCLK_PERIPH GPIO_PERIPH_A
+
+
+/*-------------------------------------------------------------------------*\
+ * data types
+\*-------------------------------------------------------------------------*/
+/* SSC clocking data */
+struct ssc_clock_data {
+ /* CMR div */
+ unsigned int cmr_div;
+
+ /* Frame period (as needed by xCMR.PERIOD) */
+ unsigned int period;
+
+ /* The SSC clock rate these settings where calculated for */
+ unsigned long ssc_rate;
+};
+
+
+/*-------------------------------------------------------------------------*\
+ * module data
+\*-------------------------------------------------------------------------*/
+static struct clk *_gclk0;
+static struct clk *_pll0;
+
+#define CODEC_CLK (_gclk0)
+
+
+/*-------------------------------------------------------------------------*\
+ * Sound SOC operations
+\*-------------------------------------------------------------------------*/
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ struct ssc_clock_data cd;
+ unsigned int rate, width_bits, channels;
+ unsigned int bitrate, ssc_div;
+ unsigned actual_rate;
+
+
+ /*
+ * Figure out required bitrate
+ */
+ rate = params_rate(params);
+ channels = params_channels(params);
+ width_bits = snd_pcm_format_physical_width(params_format(params));
+ bitrate = rate * width_bits * channels;
+
+
+ /*
+ * Figure out required SSC divider and period for required bitrate
+ */
+ cd.ssc_rate = clk_get_rate(ssc->clk);
+ ssc_div = cd.ssc_rate / bitrate;
+ cd.cmr_div = ssc_div / 2;
+ if (ssc_div & 1) {
+ /* round cmr_div up */
+ cd.cmr_div++;
+ }
+ cd.period = width_bits - 1;
+
+
+ /*
+ * Find actual rate, compare to requested rate
+ */
+ actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
+ pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+ rate, actual_rate);
+
+
+ return cd;
+}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+
+static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
+ int ret;
+
+
+ /* Due to difficulties with getting the correct clocks from the AT32's
+ * PLL0, we're going to let the CODEC be in charge of all the clocks
+ */
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+#else
+ struct ssc_clock_data cd;
+ const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+#endif
+
+ if (ssc == NULL) {
+ pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
+ return -EINVAL;
+ }
+
+
+ /*
+ * Figure out PLL and BCLK dividers for WM8510
+ */
+ switch (params_rate(params)) {
+ case 48000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_1;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 44100:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_1;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 22050:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_2;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 16000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_3;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 11025:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_4;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 8000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_6;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ default:
+ pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+
+ /*
+ * set CPU and CODEC DAI configuration
+ */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CODEC DAI format (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CPU DAI format (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ /*
+ * Set CPU clock configuration
+ */
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
+ pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
+ cd.cmr_div, cd.period);
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
+ cd.period);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CPU transmit period (%d)\n",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+ /*
+ * Set CODEC clock configuration
+ */
+ pr_debug("playpaq_wm8510: "
+ "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
+ clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
+
+
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
+ if (ret < 0) {
+ pr_warning
+ ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ clk_get_rate(CODEC_CLK), pll_out);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ return 0;
+}
+
+
+
+static struct snd_soc_ops playpaq_wm8510_ops = {
+ .hw_params = playpaq_wm8510_hw_params,
+};
+
+
+
+static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+
+
+static const char *intercon[][3] = {
+ /* speaker connected to SPKOUT */
+ {"Ext Spk", NULL, "SPKOUTP"},
+ {"Ext Spk", NULL, "SPKOUTN"},
+
+ {"Mic Bias", NULL, "Int Mic"},
+ {"MICN", NULL, "Mic Bias"},
+ {"MICP", NULL, "Mic Bias"},
+
+ /* Terminator */
+ {NULL, NULL, NULL},
+};
+
+
+
+static int playpaq_wm8510_init(struct snd_soc_codec *codec)
+{
+ int i;
+
+ /*
+ * Add DAPM widgets
+ */
+ for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
+ snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
+
+
+
+ /*
+ * Setup 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]);
+ }
+
+
+ /* always connected pins */
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ snd_soc_dapm_sync(codec);
+
+
+
+ /* Make CSB show PLL rate */
+ snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
+ WM8510_OPCLKDIV_1 | 4);
+
+ return 0;
+}
+
+
+
+static struct snd_soc_dai_link playpaq_wm8510_dai = {
+ .name = "WM8510",
+ .stream_name = "WM8510 PCM",
+ .cpu_dai = &at32_ssc_dai[0],
+ .codec_dai = &wm8510_dai,
+ .init = playpaq_wm8510_init,
+ .ops = &playpaq_wm8510_ops,
+};
+
+
+
+static struct snd_soc_machine snd_soc_machine_playpaq = {
+ .name = "LRS_PlayPaq_WM8510",
+ .dai_link = &playpaq_wm8510_dai,
+ .num_links = 1,
+};
+
+
+
+static struct wm8510_setup_data playpaq_wm8510_setup = {
+ .i2c_bus = 0,
+ .i2c_address = 0x1a,
+};
+
+
+
+static struct snd_soc_device playpaq_wm8510_snd_devdata = {
+ .machine = &snd_soc_machine_playpaq,
+ .platform = &at32_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8510,
+ .codec_data = &playpaq_wm8510_setup,
+};
+
+static struct platform_device *playpaq_snd_device;
+
+
+static int __init playpaq_asoc_init(void)
+{
+ int ret = 0;
+ struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+ struct ssc_device *ssc = NULL;
+
+
+ /*
+ * Request SSC device
+ */
+ ssc = ssc_request(0);
+ if (IS_ERR(ssc)) {
+ ret = PTR_ERR(ssc);
+ goto err_ssc;
+ }
+ ssc_p->ssc = ssc;
+
+
+ /*
+ * Configure MCLK for WM8510
+ */
+ _gclk0 = clk_get(NULL, "gclk0");
+ if (IS_ERR(_gclk0)) {
+ _gclk0 = NULL;
+ goto err_gclk0;
+ }
+ _pll0 = clk_get(NULL, "pll0");
+ if (IS_ERR(_pll0)) {
+ _pll0 = NULL;
+ goto err_pll0;
+ }
+ if (clk_set_parent(_gclk0, _pll0)) {
+ pr_warning("snd-soc-playpaq: "
+ "Failed to set PLL0 as parent for DAC clock\n");
+ goto err_set_clk;
+ }
+ clk_set_rate(CODEC_CLK, 12000000);
+ clk_enable(CODEC_CLK);
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+ at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
+#endif
+
+
+ /*
+ * Create and register platform device
+ */
+ playpaq_snd_device = platform_device_alloc("soc-audio", 0);
+ if (playpaq_snd_device == NULL) {
+ ret = -ENOMEM;
+ goto err_device_alloc;
+ }
+
+ platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
+ playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
+
+ ret = platform_device_add(playpaq_snd_device);
+ if (ret) {
+ pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
+ ret);
+ goto err_device_add;
+ }
+
+ return 0;
+
+
+err_device_add:
+ if (playpaq_snd_device != NULL) {
+ platform_device_put(playpaq_snd_device);
+ playpaq_snd_device = NULL;
+ }
+err_device_alloc:
+err_set_clk:
+ if (_pll0 != NULL) {
+ clk_put(_pll0);
+ _pll0 = NULL;
+ }
+err_pll0:
+ if (_gclk0 != NULL) {
+ clk_put(_gclk0);
+ _gclk0 = NULL;
+ }
+err_gclk0:
+ ssc_free(ssc);
+err_ssc:
+ return ret;
+}
+
+
+static void __exit playpaq_asoc_exit(void)
+{
+ struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+ struct ssc_device *ssc;
+
+ if (ssc_p != NULL) {
+ ssc = ssc_p->ssc;
+ if (ssc != NULL)
+ ssc_free(ssc);
+ ssc_p->ssc = NULL;
+ }
+
+ if (_gclk0 != NULL) {
+ clk_put(_gclk0);
+ _gclk0 = NULL;
+ }
+ if (_pll0 != NULL) {
+ clk_put(_pll0);
+ _pll0 = NULL;
+ }
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+ at32_free_pin(MCLK_PIN);
+#endif
+
+ platform_device_unregister(playpaq_snd_device);
+ playpaq_snd_device = NULL;
+}
+
+module_init(playpaq_asoc_init);
+module_exit(playpaq_asoc_exit);
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
index 5cb93fd3a40..85a883299c2 100644
--- a/sound/soc/at91/Kconfig
+++ b/sound/soc/at91/Kconfig
@@ -1,6 +1,6 @@
config SND_AT91_SOC
tristate "SoC Audio for the Atmel AT91 System-on-Chip"
- depends on ARCH_AT91 && SND_SOC
+ depends on ARCH_AT91
help
Say Y or M if you want to add support for codecs attached to
the AT91 SSC interface. You will also need
@@ -8,20 +8,3 @@ config SND_AT91_SOC
config SND_AT91_SOC_SSC
tristate
-
-config SND_AT91_SOC_ETI_B1_WM8731
- tristate "SoC Audio support for WM8731-based Endrelia ETI-B1 boards"
- depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1)
- select SND_AT91_SOC_SSC
- select SND_SOC_WM8731
- help
- Say Y if you want to add support for SoC audio on WM8731-based
- Endrelia Technologies Inc ETI-B1 or ETI-C1 boards.
-
-config SND_AT91_SOC_ETI_SLAVE
- bool "Run codec in slave Mode on Endrelia boards"
- depends on SND_AT91_SOC_ETI_B1_WM8731
- default n
- help
- Say Y if you want to run with the AT91 SSC generating the BCLK
- and LRC signals on Endrelia boards.
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
index f23da17cc32..b817f11df28 100644
--- a/sound/soc/at91/Makefile
+++ b/sound/soc/at91/Makefile
@@ -4,8 +4,3 @@ snd-soc-at91-ssc-objs := at91-ssc.o
obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o
-
-# AT91 Machine Support
-snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o
-
-obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
index ccac6bd2889..7ab48bd25e4 100644
--- a/sound/soc/at91/at91-pcm.c
+++ b/sound/soc/at91/at91-pcm.c
@@ -28,8 +28,8 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/at91_ssc.h>
+#include <mach/hardware.h>
+#include <mach/at91_ssc.h>
#include "at91-pcm.h"
@@ -318,7 +318,7 @@ static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
static u64 at91_pcm_dmamask = 0xffffffff;
static int at91_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret = 0;
@@ -367,7 +367,7 @@ static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
#ifdef CONFIG_PM
static int at91_pcm_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = dai->runtime;
struct at91_runtime_data *prtd;
@@ -392,7 +392,7 @@ static int at91_pcm_suspend(struct platform_device *pdev,
}
static int at91_pcm_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = dai->runtime;
struct at91_runtime_data *prtd;
diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h
index 58d0f00a07b..e5aada2cb10 100644
--- a/sound/soc/at91/at91-pcm.h
+++ b/sound/soc/at91/at91-pcm.h
@@ -19,7 +19,7 @@
#ifndef _AT91_PCM_H
#define _AT91_PCM_H
-#include <asm/arch/hardware.h>
+#include <mach/hardware.h>
struct at91_ssc_periph {
void __iomem *base;
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index bc35d00a38f..1b61cc46126 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -5,7 +5,7 @@
* Endrelia Technologies Inc.
*
* Based on pxa2xx Platform drivers by
- * Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -28,9 +28,9 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/at91_pmc.h>
-#include <asm/arch/at91_ssc.h>
+#include <mach/hardware.h>
+#include <mach/at91_pmc.h>
+#include <mach/at91_ssc.h>
#include "at91-pcm.h"
#include "at91-ssc.h"
@@ -41,7 +41,7 @@
#define DBG(x...)
#endif
-#if defined(CONFIG_ARCH_AT91SAM9260)
+#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
#define NUM_SSC_DEVICES 1
#else
#define NUM_SSC_DEVICES 3
@@ -281,7 +281,7 @@ static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
/*
* Record the SSC system clock rate.
*/
-static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
/*
@@ -303,7 +303,7 @@ static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
/*
* Record the DAI format for use in hw_params().
*/
-static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -315,7 +315,7 @@ static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
/*
* Record SSC clock dividers for use in hw_params().
*/
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -408,7 +408,7 @@ static int at91_ssc_hw_params(struct snd_pcm_substream *substream,
dma_params->pdc_xfer_size = 4;
break;
default:
- printk(KERN_WARNING "at91-ssc: unsupported PCM format");
+ printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");
return -EINVAL;
}
@@ -634,7 +634,7 @@ static int at91_ssc_prepare(struct snd_pcm_substream *substream)
#ifdef CONFIG_PM
static int at91_ssc_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
struct at91_ssc_info *ssc_p;
@@ -662,7 +662,7 @@ static int at91_ssc_suspend(struct platform_device *pdev,
}
static int at91_ssc_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
struct at91_ssc_info *ssc_p;
@@ -700,7 +700,7 @@ static int at91_ssc_resume(struct platform_device *pdev,
#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-struct snd_soc_cpu_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
+struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
{ .name = "at91-ssc0",
.id = 0,
.type = SND_SOC_DAI_PCM,
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
index b188f973df9..6b7bf382d06 100644
--- a/sound/soc/at91/at91-ssc.h
+++ b/sound/soc/at91/at91-ssc.h
@@ -21,7 +21,7 @@
#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
-extern struct snd_soc_cpu_dai at91_ssc_dai[];
+extern struct snd_soc_dai at91_ssc_dai[];
#endif /* _AT91_SSC_H */
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
deleted file mode 100644
index 1347dcf3f80..00000000000
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board.
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- * Endrelia Technologies Inc.
- * Created: Mar 29, 2006
- *
- * Based on corgi.c by:
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- *
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
- * Richard Purdie <richard@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#include <asm/hardware.h>
-#include <asm/arch/gpio.h>
-
-#include "../codecs/wm8731.h"
-#include "at91-pcm.h"
-#include "at91-ssc.h"
-
-#if 0
-#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x)
-#else
-#define DBG(x...)
-#endif
-
-static struct clk *pck1_clk;
-static struct clk *pllb_clk;
-
-
-static int eti_b1_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
- int ret;
-
- /* cpu clock is the AT91 master clock sent to the SSC */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
- 60000000, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* codec system clock is supplied by PCK1, set to 12MHz */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK,
- 12000000, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* Start PCK1 clock. */
- clk_enable(pck1_clk);
- DBG("pck1 started\n");
-
- return 0;
-}
-
-static void eti_b1_shutdown(struct snd_pcm_substream *substream)
-{
- /* Stop PCK1 clock. */
- clk_disable(pck1_clk);
- DBG("pck1 stopped\n");
-}
-
-static int eti_b1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
- int ret;
-
-#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
- unsigned int rate;
- int cmr_div, period;
-
- /* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /*
- * The SSC clock dividers depend on the sample rate. The CMR.DIV
- * field divides the system master clock MCK to drive the SSC TK
- * signal which provides the codec BCLK. The TCMR.PERIOD and
- * RCMR.PERIOD fields further divide the BCLK signal to drive
- * the SSC TF and RF signals which provide the codec DACLRC and
- * ADCLRC clocks.
- *
- * The dividers were determined through trial and error, where a
- * CMR.DIV value is chosen such that the resulting BCLK value is
- * divisible, or almost divisible, by (2 * sample rate), and then
- * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
- */
- rate = params_rate(params);
-
- switch (rate) {
- case 8000:
- cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */
- period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */
- break;
- case 32000:
- cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */
- period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */
- break;
- case 48000:
- cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */
- period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */
- break;
- default:
- printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate);
- return -EINVAL;
- }
-
- /* set the MCK divider for BCLK */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
- if (ret < 0)
- return ret;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* set the BCLK divider for DACLRC */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
- AT91SSC_TCMR_PERIOD, period);
- } else {
- /* set the BCLK divider for ADCLRC */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
- AT91SSC_RCMR_PERIOD, period);
- }
- if (ret < 0)
- return ret;
-
-#else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */
- /*
- * Codec in Master Mode.
- */
-
- /* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
-#endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */
-
- return 0;
-}
-
-static struct snd_soc_ops eti_b1_ops = {
- .startup = eti_b1_startup,
- .hw_params = eti_b1_hw_params,
- .shutdown = eti_b1_shutdown,
-};
-
-
-static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-};
-
-static const char *intercon[][3] = {
-
- /* speaker connected to LHPOUT */
- {"Ext Spk", NULL, "LHPOUT"},
-
- /* mic is connected to Mic Jack, with WM8731 Mic Bias */
- {"MICIN", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Int Mic"},
-
- /* terminator */
- {NULL, NULL, NULL},
-};
-
-/*
- * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
- */
-static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
-{
- int i;
-
- DBG("eti_b1_wm8731_init() called\n");
-
- /* Add specific widgets */
- for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) {
- snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]);
- }
-
- /* Set up specific audio path interconnects */
- for(i = 0; intercon[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, intercon[i][0],
- intercon[i][1], intercon[i][2]);
- }
-
- /* not connected */
- snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
-
- /* always connected */
- snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
-
- snd_soc_dapm_sync_endpoints(codec);
-
- return 0;
-}
-
-static struct snd_soc_dai_link eti_b1_dai = {
- .name = "WM8731",
- .stream_name = "WM8731 PCM",
- .cpu_dai = &at91_ssc_dai[1],
- .codec_dai = &wm8731_dai,
- .init = eti_b1_wm8731_init,
- .ops = &eti_b1_ops,
-};
-
-static struct snd_soc_machine snd_soc_machine_eti_b1 = {
- .name = "ETI_B1_WM8731",
- .dai_link = &eti_b1_dai,
- .num_links = 1,
-};
-
-static struct wm8731_setup_data eti_b1_wm8731_setup = {
- .i2c_address = 0x1a,
-};
-
-static struct snd_soc_device eti_b1_snd_devdata = {
- .machine = &snd_soc_machine_eti_b1,
- .platform = &at91_soc_platform,
- .codec_dev = &soc_codec_dev_wm8731,
- .codec_data = &eti_b1_wm8731_setup,
-};
-
-static struct platform_device *eti_b1_snd_device;
-
-static int __init eti_b1_init(void)
-{
- int ret;
- struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
-
- if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) {
- DBG("SSC1 memory region is busy\n");
- return -EBUSY;
- }
-
- ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K);
- if (!ssc->base) {
- DBG("SSC1 memory ioremap failed\n");
- ret = -ENOMEM;
- goto fail_release_mem;
- }
-
- ssc->pid = AT91RM9200_ID_SSC1;
-
- eti_b1_snd_device = platform_device_alloc("soc-audio", -1);
- if (!eti_b1_snd_device) {
- DBG("platform device allocation failed\n");
- ret = -ENOMEM;
- goto fail_io_unmap;
- }
-
- platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata);
- eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev;
-
- ret = platform_device_add(eti_b1_snd_device);
- if (ret) {
- DBG("platform device add failed\n");
- platform_device_put(eti_b1_snd_device);
- goto fail_io_unmap;
- }
-
- at91_set_A_periph(AT91_PIN_PB6, 0); /* TF1 */
- at91_set_A_periph(AT91_PIN_PB7, 0); /* TK1 */
- at91_set_A_periph(AT91_PIN_PB8, 0); /* TD1 */
- at91_set_A_periph(AT91_PIN_PB9, 0); /* RD1 */
-/* at91_set_A_periph(AT91_PIN_PB10, 0);*/ /* RK1 */
- at91_set_A_periph(AT91_PIN_PB11, 0); /* RF1 */
-
- /*
- * Set PCK1 parent to PLLB and its rate to 12 Mhz.
- */
- pllb_clk = clk_get(NULL, "pllb");
- pck1_clk = clk_get(NULL, "pck1");
-
- clk_set_parent(pck1_clk, pllb_clk);
- clk_set_rate(pck1_clk, 12000000);
-
- DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk));
-
- /* assign the GPIO pin to PCK1 */
- at91_set_B_periph(AT91_PIN_PA24, 0);
-
-#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
- printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n");
-#else
- printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n");
-#endif
- return ret;
-
-fail_io_unmap:
- iounmap(ssc->base);
-fail_release_mem:
- release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
- return ret;
-}
-
-static void __exit eti_b1_exit(void)
-{
- struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
-
- clk_put(pck1_clk);
- clk_put(pllb_clk);
-
- platform_device_unregister(eti_b1_snd_device);
-
- iounmap(ssc->base);
- release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
-}
-
-module_init(eti_b1_init);
-module_exit(eti_b1_exit);
-
-/* Module information */
-MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
-MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
new file mode 100644
index 00000000000..410a893aa66
--- /dev/null
+++ b/sound/soc/au1x/Kconfig
@@ -0,0 +1,32 @@
+##
+## Au1200/Au1550 PSC + DBDMA
+##
+config SND_SOC_AU1XPSC
+ tristate "SoC Audio for Au1200/Au1250/Au1550"
+ depends on SOC_AU1200 || SOC_AU1550
+ help
+ This option enables support for the Programmable Serial
+ Controllers in AC97 and I2S mode, and the Descriptor-Based DMA
+ Controller (DBDMA) as found on the Au1200/Au1250/Au1550 SoC.
+
+config SND_SOC_AU1XPSC_I2S
+ tristate
+
+config SND_SOC_AU1XPSC_AC97
+ tristate
+ select AC97_BUS
+ select SND_AC97_CODEC
+ select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_SAMPLE_PSC_AC97
+ tristate "Sample Au12x0/Au1550 PSC AC97 sound machine"
+ depends on SND_SOC_AU1XPSC
+ select SND_SOC_AU1XPSC_AC97
+ select SND_SOC_AC97_CODEC
+ help
+ This is a sample AC97 sound machine for use in Au12x0/Au1550
+ based systems which have audio on PSC1 (e.g. Db1200 demoboard).
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
new file mode 100644
index 00000000000..6c6950b8003
--- /dev/null
+++ b/sound/soc/au1x/Makefile
@@ -0,0 +1,13 @@
+# Au1200/Au1550 PSC audio
+snd-soc-au1xpsc-dbdma-objs := dbdma2.o
+snd-soc-au1xpsc-i2s-objs := psc-i2s.o
+snd-soc-au1xpsc-ac97-objs := psc-ac97.o
+
+obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
+
+# Boards
+snd-soc-sample-ac97-objs := sample-ac97.o
+
+obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
new file mode 100644
index 00000000000..1466d932880
--- /dev/null
+++ b/sound/soc/au1x/dbdma2.c
@@ -0,0 +1,421 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * DMA glue for Au1x-PSC audio.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/*#define PCM_DEBUG*/
+
+#define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x)
+#ifdef PCM_DEBUG
+#define DBG MSG
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+struct au1xpsc_audio_dmadata {
+ /* DDMA control data */
+ unsigned int ddma_id; /* DDMA direction ID for this PSC */
+ u32 ddma_chan; /* DDMA context */
+
+ /* PCM context (for irq handlers) */
+ struct snd_pcm_substream *substream;
+ unsigned long curr_period; /* current segment DDMA is working on */
+ unsigned long q_period; /* queue period(s) */
+ unsigned long dma_area; /* address of queued DMA area */
+ unsigned long dma_area_s; /* start address of DMA area */
+ unsigned long pos; /* current byte position being played */
+ unsigned long periods; /* number of SG segments in total */
+ unsigned long period_bytes; /* size in bytes of one SG segment */
+
+ /* runtime data */
+ int msbits;
+};
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2];
+
+/*
+ * These settings are somewhat okay, at least on my machine audio plays
+ * almost skip-free. Especially the 64kB buffer seems to help a LOT.
+ */
+#define AU1XPSC_PERIOD_MIN_BYTES 1024
+#define AU1XPSC_BUFFER_MIN_BYTES 65536
+
+#define AU1XPSC_PCM_FMTS \
+ (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_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \
+ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \
+ 0)
+
+/* PCM hardware DMA capabilities - platform specific */
+static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = AU1XPSC_PCM_FMTS,
+ .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
+ .period_bytes_max = 4096 * 1024 - 1,
+ .periods_min = 2,
+ .periods_max = 4096, /* 2 to as-much-as-you-like */
+ .buffer_bytes_max = 4096 * 1024 - 1,
+ .fifo_size = 16, /* fifo entries of AC97/I2S PSC */
+};
+
+static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
+{
+ au1xxx_dbdma_put_source_flags(cd->ddma_chan,
+ (void *)phys_to_virt(cd->dma_area),
+ cd->period_bytes, DDMA_FLAGS_IE);
+
+ /* update next-to-queue period */
+ ++cd->q_period;
+ cd->dma_area += cd->period_bytes;
+ if (cd->q_period >= cd->periods) {
+ cd->q_period = 0;
+ cd->dma_area = cd->dma_area_s;
+ }
+}
+
+static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
+{
+ au1xxx_dbdma_put_dest_flags(cd->ddma_chan,
+ (void *)phys_to_virt(cd->dma_area),
+ cd->period_bytes, DDMA_FLAGS_IE);
+
+ /* update next-to-queue period */
+ ++cd->q_period;
+ cd->dma_area += cd->period_bytes;
+ if (cd->q_period >= cd->periods) {
+ cd->q_period = 0;
+ cd->dma_area = cd->dma_area_s;
+ }
+}
+
+static void au1x_pcm_dmatx_cb(int irq, void *dev_id)
+{
+ struct au1xpsc_audio_dmadata *cd = dev_id;
+
+ cd->pos += cd->period_bytes;
+ if (++cd->curr_period >= cd->periods) {
+ cd->pos = 0;
+ cd->curr_period = 0;
+ }
+ snd_pcm_period_elapsed(cd->substream);
+ au1x_pcm_queue_tx(cd);
+}
+
+static void au1x_pcm_dmarx_cb(int irq, void *dev_id)
+{
+ struct au1xpsc_audio_dmadata *cd = dev_id;
+
+ cd->pos += cd->period_bytes;
+ if (++cd->curr_period >= cd->periods) {
+ cd->pos = 0;
+ cd->curr_period = 0;
+ }
+ snd_pcm_period_elapsed(cd->substream);
+ au1x_pcm_queue_rx(cd);
+}
+
+static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd)
+{
+ if (pcd->ddma_chan) {
+ au1xxx_dbdma_stop(pcd->ddma_chan);
+ au1xxx_dbdma_reset(pcd->ddma_chan);
+ au1xxx_dbdma_chan_free(pcd->ddma_chan);
+ pcd->ddma_chan = 0;
+ pcd->msbits = 0;
+ }
+}
+
+/* in case of missing DMA ring or changed TX-source / RX-dest bit widths,
+ * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according
+ * to ALSA-supplied sample depth. This is due to limitations in the dbdma api
+ * (cannot adjust source/dest widths of already allocated descriptor ring).
+ */
+static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
+ int stype, int msbits)
+{
+ /* DMA only in 8/16/32 bit widths */
+ if (msbits == 24)
+ msbits = 32;
+
+ /* check current config: correct bits and descriptors allocated? */
+ if ((pcd->ddma_chan) && (msbits == pcd->msbits))
+ goto out; /* all ok! */
+
+ au1x_pcm_dbdma_free(pcd);
+
+ if (stype == PCM_RX)
+ pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
+ DSCR_CMD0_ALWAYS,
+ au1x_pcm_dmarx_cb, (void *)pcd);
+ else
+ pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
+ pcd->ddma_id,
+ au1x_pcm_dmatx_cb, (void *)pcd);
+
+ if (!pcd->ddma_chan)
+ return -ENOMEM;;
+
+ au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits);
+ au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2);
+
+ pcd->msbits = msbits;
+
+ au1xxx_dbdma_stop(pcd->ddma_chan);
+ au1xxx_dbdma_reset(pcd->ddma_chan);
+
+out:
+ return 0;
+}
+
+static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct au1xpsc_audio_dmadata *pcd;
+ int stype, ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ goto out;
+
+ stype = SUBSTREAM_TYPE(substream);
+ pcd = au1xpsc_audio_pcmdma[stype];
+
+ DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
+ "runtime->min_align %d\n",
+ (unsigned long)runtime->dma_area,
+ (unsigned long)runtime->dma_addr, runtime->dma_bytes,
+ runtime->min_align);
+
+ DBG("bits %d frags %d frag_bytes %d is_rx %d\n", params->msbits,
+ params_periods(params), params_period_bytes(params), stype);
+
+ ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
+ if (ret) {
+ MSG("DDMA channel (re)alloc failed!\n");
+ goto out;
+ }
+
+ pcd->substream = substream;
+ pcd->period_bytes = params_period_bytes(params);
+ pcd->periods = params_periods(params);
+ pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr;
+ pcd->q_period = 0;
+ pcd->curr_period = 0;
+ pcd->pos = 0;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct au1xpsc_audio_dmadata *pcd =
+ au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)];
+
+ au1xxx_dbdma_reset(pcd->ddma_chan);
+
+ if (SUBSTREAM_TYPE(substream) == PCM_RX) {
+ au1x_pcm_queue_rx(pcd);
+ au1x_pcm_queue_rx(pcd);
+ } else {
+ au1x_pcm_queue_tx(pcd);
+ au1x_pcm_queue_tx(pcd);
+ }
+
+ return 0;
+}
+
+static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ au1xxx_dbdma_start(c);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ au1xxx_dbdma_stop(c);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t
+au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ return bytes_to_frames(substream->runtime,
+ au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos);
+}
+
+static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
+ return 0;
+}
+
+static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+{
+ au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]);
+ return 0;
+}
+
+struct snd_pcm_ops au1xpsc_pcm_ops = {
+ .open = au1xpsc_pcm_open,
+ .close = au1xpsc_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = au1xpsc_pcm_hw_params,
+ .hw_free = au1xpsc_pcm_hw_free,
+ .prepare = au1xpsc_pcm_prepare,
+ .trigger = au1xpsc_pcm_trigger,
+ .pointer = au1xpsc_pcm_pointer,
+};
+
+static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int au1xpsc_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
+
+ return 0;
+}
+
+static int au1xpsc_pcm_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ int ret;
+
+ if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX])
+ return -EBUSY;
+
+ /* TX DMA */
+ au1xpsc_audio_pcmdma[PCM_TX]
+ = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+ if (!au1xpsc_audio_pcmdma[PCM_TX])
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ ret = -ENODEV;
+ goto out1;
+ }
+ (au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start;
+
+ /* RX DMA */
+ au1xpsc_audio_pcmdma[PCM_RX]
+ = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+ if (!au1xpsc_audio_pcmdma[PCM_RX])
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!r) {
+ ret = -ENODEV;
+ goto out2;
+ }
+ (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
+
+ return 0;
+
+out2:
+ kfree(au1xpsc_audio_pcmdma[PCM_RX]);
+ au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+out1:
+ kfree(au1xpsc_audio_pcmdma[PCM_TX]);
+ au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+ return ret;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (au1xpsc_audio_pcmdma[i]) {
+ au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
+ kfree(au1xpsc_audio_pcmdma[i]);
+ au1xpsc_audio_pcmdma[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+ .name = "au1xpsc-pcm-dbdma",
+ .probe = au1xpsc_pcm_probe,
+ .remove = au1xpsc_pcm_remove,
+ .pcm_ops = &au1xpsc_pcm_ops,
+ .pcm_new = au1xpsc_pcm_new,
+ .pcm_free = au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __init au1xpsc_audio_dbdma_init(void)
+{
+ au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+ au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+ return 0;
+}
+
+static void __exit au1xpsc_audio_dbdma_exit(void)
+{
+}
+
+module_init(au1xpsc_audio_dbdma_init);
+module_exit(au1xpsc_audio_dbdma_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
new file mode 100644
index 00000000000..57facbad682
--- /dev/null
+++ b/sound/soc/au1x/psc-ac97.c
@@ -0,0 +1,387 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * Au1xxx-PSC AC97 glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+#define AC97_DIR \
+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES \
+ SNDRV_PCM_RATE_8000_48000
+
+#define AC97_FMTS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
+
+#define AC97PCR_START(stype) \
+ ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
+#define AC97PCR_STOP(stype) \
+ ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
+#define AC97PCR_CLRFIFO(stype) \
+ ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
+
+/* AC97 controller reads codec register */
+static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ unsigned short data, tmo;
+
+ au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+ au_sync();
+
+ tmo = 1000;
+ while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+ udelay(2);
+
+ if (!tmo)
+ data = 0xffff;
+ else
+ data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ au_sync();
+
+ return data;
+}
+
+/* AC97 controller writes to codec register */
+static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ unsigned int tmo;
+
+ au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+ au_sync();
+ tmo = 1000;
+ while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+ au_sync();
+
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ au_sync();
+}
+
+/* AC97 controller asserts a warm reset */
+static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+
+ au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
+ au_sync();
+ msleep(10);
+ au_writel(0, AC97_RST(pscdata));
+ au_sync();
+}
+
+static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ int i;
+
+ /* disable PSC during cold reset */
+ au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
+ au_sync();
+
+ /* issue cold reset */
+ au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
+ au_sync();
+ msleep(500);
+ au_writel(0, AC97_RST(pscdata));
+ au_sync();
+
+ /* enable PSC */
+ au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+ au_sync();
+
+ /* wait for PSC to indicate it's ready */
+ i = 100000;
+ while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
+ au_sync();
+
+ if (i == 0) {
+ printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
+ return;
+ }
+
+ /* enable the ac97 function */
+ au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ /* wait for AC97 core to become ready */
+ i = 100000;
+ while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
+ au_sync();
+ if (i == 0)
+ printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = au1xpsc_ac97_read,
+ .write = au1xpsc_ac97_write,
+ .reset = au1xpsc_ac97_cold_reset,
+ .warm_reset = au1xpsc_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ unsigned long r, stat;
+ int chans, stype = SUBSTREAM_TYPE(substream);
+
+ chans = params_channels(params);
+
+ r = au_readl(AC97_CFG(pscdata));
+ stat = au_readl(AC97_STAT(pscdata));
+
+ /* already active? */
+ if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
+ /* reject parameters not currently set up */
+ if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
+ (pscdata->rate != params_rate(params)))
+ return -EINVAL;
+ } else {
+ /* disable AC97 device controller first */
+ au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
+ r &= ~PSC_AC97CFG_LEN_MASK;
+ r |= PSC_AC97CFG_SET_LEN(params->msbits);
+
+ /* channels: enable slots for front L/R channel */
+ if (stype == PCM_TX) {
+ r &= ~PSC_AC97CFG_TXSLOT_MASK;
+ r |= PSC_AC97CFG_TXSLOT_ENA(3);
+ r |= PSC_AC97CFG_TXSLOT_ENA(4);
+ } else {
+ r &= ~PSC_AC97CFG_RXSLOT_MASK;
+ r |= PSC_AC97CFG_RXSLOT_ENA(3);
+ r |= PSC_AC97CFG_RXSLOT_ENA(4);
+ }
+
+ /* finally enable the AC97 controller again */
+ au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ pscdata->cfg = r;
+ pscdata->rate = params_rate(params);
+ }
+
+ return 0;
+}
+
+static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ int ret, stype = SUBSTREAM_TYPE(substream);
+
+ ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
+ au_sync();
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
+ au_sync();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int au1xpsc_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct resource *r;
+ unsigned long sel;
+
+ if (au1xpsc_ac97_workdata)
+ return -EBUSY;
+
+ au1xpsc_ac97_workdata =
+ kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ if (!au1xpsc_ac97_workdata)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -ENODEV;
+ goto out0;
+ }
+
+ ret = -EBUSY;
+ au1xpsc_ac97_workdata->ioarea =
+ request_mem_region(r->start, r->end - r->start + 1,
+ "au1xpsc_ac97");
+ if (!au1xpsc_ac97_workdata->ioarea)
+ goto out0;
+
+ au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
+ if (!au1xpsc_ac97_workdata->mmio)
+ goto out1;
+
+ /* configuration: max dma trigger threshold, enable ac97 */
+ au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+ PSC_AC97CFG_TT_FIFO8 |
+ PSC_AC97CFG_DE_ENABLE;
+
+ /* preserve PSC clock source set up by platform (dev.platform_data
+ * is already occupied by soc layer)
+ */
+ sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
+ au_sync();
+ /* next up: cold reset. Dont check for PSC-ready now since
+ * there may not be any codec clock yet.
+ */
+
+ return 0;
+
+out1:
+ release_resource(au1xpsc_ac97_workdata->ioarea);
+ kfree(au1xpsc_ac97_workdata->ioarea);
+out0:
+ kfree(au1xpsc_ac97_workdata);
+ au1xpsc_ac97_workdata = NULL;
+ return ret;
+}
+
+static void au1xpsc_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ /* disable PSC completely */
+ au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_sync();
+
+ iounmap(au1xpsc_ac97_workdata->mmio);
+ release_resource(au1xpsc_ac97_workdata->ioarea);
+ kfree(au1xpsc_ac97_workdata->ioarea);
+ kfree(au1xpsc_ac97_workdata);
+ au1xpsc_ac97_workdata = NULL;
+}
+
+static int au1xpsc_ac97_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ /* save interesting registers and disable PSC */
+ au1xpsc_ac97_workdata->pm[0] =
+ au_readl(PSC_SEL(au1xpsc_ac97_workdata));
+
+ au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_sync();
+
+ return 0;
+}
+
+static int au1xpsc_ac97_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ /* restore PSC clock config */
+ au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
+ PSC_SEL(au1xpsc_ac97_workdata));
+ au_sync();
+
+ /* after this point the ac97 core will cold-reset the codec.
+ * During cold-reset the PSC is reinitialized and the last
+ * configuration set up in hw_params() is restored.
+ */
+ return 0;
+}
+
+struct snd_soc_dai au1xpsc_ac97_dai = {
+ .name = "au1xpsc_ac97",
+ .type = SND_SOC_DAI_AC97,
+ .probe = au1xpsc_ac97_probe,
+ .remove = au1xpsc_ac97_remove,
+ .suspend = au1xpsc_ac97_suspend,
+ .resume = au1xpsc_ac97_resume,
+ .playback = {
+ .rates = AC97_RATES,
+ .formats = AC97_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .rates = AC97_RATES,
+ .formats = AC97_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = {
+ .trigger = au1xpsc_ac97_trigger,
+ .hw_params = au1xpsc_ac97_hw_params,
+ },
+};
+EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
+
+static int __init au1xpsc_ac97_init(void)
+{
+ au1xpsc_ac97_workdata = NULL;
+ return 0;
+}
+
+static void __exit au1xpsc_ac97_exit(void)
+{
+}
+
+module_init(au1xpsc_ac97_init);
+module_exit(au1xpsc_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
new file mode 100644
index 00000000000..9384702c7eb
--- /dev/null
+++ b/sound/soc/au1x/psc-i2s.c
@@ -0,0 +1,414 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * Au1xxx-PSC I2S glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ * NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/* supported I2S DAI hardware formats */
+#define AU1XPSC_I2S_DAIFMT \
+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | \
+ SND_SOC_DAIFMT_NB_NF)
+
+/* supported I2S direction */
+#define AU1XPSC_I2S_DIR \
+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AU1XPSC_I2S_RATES \
+ SNDRV_PCM_RATE_8000_192000
+
+#define AU1XPSC_I2S_FMTS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+#define I2SSTAT_BUSY(stype) \
+ ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
+#define I2SPCR_START(stype) \
+ ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
+#define I2SPCR_STOP(stype) \
+ ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
+#define I2SPCR_CLRFIFO(stype) \
+ ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
+
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
+
+static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ unsigned long ct;
+ int ret;
+
+ ret = -EINVAL;
+
+ ct = pscdata->cfg;
+
+ ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ); /* left-justified */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ct |= PSC_I2SCFG_XM; /* enable I2S mode */
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ ct |= PSC_I2SCFG_MLJ; /* LSB (right-) justified */
+ break;
+ default:
+ goto out;
+ }
+
+ ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI); /* IB-IF */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ct |= PSC_I2SCFG_BI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ct |= PSC_I2SCFG_WI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ goto out;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: /* CODEC master */
+ ct |= PSC_I2SCFG_MS; /* PSC I2S slave mode */
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
+ ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */
+ break;
+ default:
+ goto out;
+ }
+
+ pscdata->cfg = ct;
+ ret = 0;
+out:
+ return ret;
+}
+
+static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+
+ int cfgbits;
+ unsigned long stat;
+
+ /* check if the PSC is already streaming data */
+ stat = au_readl(I2S_STAT(pscdata));
+ if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
+ /* reject parameters not currently set up in hardware */
+ cfgbits = au_readl(I2S_CFG(pscdata));
+ if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
+ (params_rate(params) != pscdata->rate))
+ return -EINVAL;
+ } else {
+ /* set sample bitdepth */
+ pscdata->cfg &= ~(0x1f << 4);
+ pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
+ /* remember current rate for other stream */
+ pscdata->rate = params_rate(params);
+ }
+ return 0;
+}
+
+/* Configure PSC late: on my devel systems the codec is I2S master and
+ * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit. ASoC
+ * uses aggressive PM and switches the codec off when it is not in use
+ * which also means the PSC unit doesn't get any clocks and is therefore
+ * dead. That's why this chunk here gets called from the trigger callback
+ * because I can be reasonably certain the codec is driving the clocks.
+ */
+static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
+{
+ unsigned long tmo;
+
+ /* bring PSC out of sleep, and configure I2S unit */
+ au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+ au_sync();
+
+ tmo = 1000000;
+ while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
+ tmo--;
+
+ if (!tmo)
+ goto psc_err;
+
+ au_writel(0, I2S_CFG(pscdata));
+ au_sync();
+ au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
+ au_sync();
+
+ /* wait for I2S controller to become ready */
+ tmo = 1000000;
+ while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
+ tmo--;
+
+ if (tmo)
+ return 0;
+
+psc_err:
+ au_writel(0, I2S_CFG(pscdata));
+ au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+ au_sync();
+ return -ETIMEDOUT;
+}
+
+static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
+{
+ unsigned long tmo, stat;
+ int ret;
+
+ ret = 0;
+
+ /* if both TX and RX are idle, configure the PSC */
+ stat = au_readl(I2S_STAT(pscdata));
+ if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
+ ret = au1xpsc_i2s_configure(pscdata);
+ if (ret)
+ goto out;
+ }
+
+ au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
+ au_sync();
+ au_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
+ au_sync();
+
+ /* wait for start confirmation */
+ tmo = 1000000;
+ while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+ tmo--;
+
+ if (!tmo) {
+ au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+ au_sync();
+ ret = -ETIMEDOUT;
+ }
+out:
+ return ret;
+}
+
+static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
+{
+ unsigned long tmo, stat;
+
+ au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+ au_sync();
+
+ /* wait for stop confirmation */
+ tmo = 1000000;
+ while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+ tmo--;
+
+ /* if both TX and RX are idle, disable PSC */
+ stat = au_readl(I2S_STAT(pscdata));
+ if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
+ au_writel(0, I2S_CFG(pscdata));
+ au_sync();
+ au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+ au_sync();
+ }
+ return 0;
+}
+
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ int ret, stype = SUBSTREAM_TYPE(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = au1xpsc_i2s_start(pscdata, stype);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = au1xpsc_i2s_stop(pscdata, stype);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int au1xpsc_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct resource *r;
+ unsigned long sel;
+ int ret;
+
+ if (au1xpsc_i2s_workdata)
+ return -EBUSY;
+
+ au1xpsc_i2s_workdata =
+ kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ if (!au1xpsc_i2s_workdata)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -ENODEV;
+ goto out0;
+ }
+
+ ret = -EBUSY;
+ au1xpsc_i2s_workdata->ioarea =
+ request_mem_region(r->start, r->end - r->start + 1,
+ "au1xpsc_i2s");
+ if (!au1xpsc_i2s_workdata->ioarea)
+ goto out0;
+
+ au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
+ if (!au1xpsc_i2s_workdata->mmio)
+ goto out1;
+
+ /* preserve PSC clock source set up by platform (dev.platform_data
+ * is already occupied by soc layer)
+ */
+ sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
+ au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_sync();
+
+ /* preconfigure: set max rx/tx fifo depths */
+ au1xpsc_i2s_workdata->cfg |=
+ PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+
+ /* don't wait for I2S core to become ready now; clocks may not
+ * be running yet; depending on clock input for PSC a wait might
+ * time out.
+ */
+
+ return 0;
+
+out1:
+ release_resource(au1xpsc_i2s_workdata->ioarea);
+ kfree(au1xpsc_i2s_workdata->ioarea);
+out0:
+ kfree(au1xpsc_i2s_workdata);
+ au1xpsc_i2s_workdata = NULL;
+ return ret;
+}
+
+static void au1xpsc_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+
+ iounmap(au1xpsc_i2s_workdata->mmio);
+ release_resource(au1xpsc_i2s_workdata->ioarea);
+ kfree(au1xpsc_i2s_workdata->ioarea);
+ kfree(au1xpsc_i2s_workdata);
+ au1xpsc_i2s_workdata = NULL;
+}
+
+static int au1xpsc_i2s_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ /* save interesting register and disable PSC */
+ au1xpsc_i2s_workdata->pm[0] =
+ au_readl(PSC_SEL(au1xpsc_i2s_workdata));
+
+ au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+
+ return 0;
+}
+
+static int au1xpsc_i2s_resume(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ /* select I2S mode and PSC clock */
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(au1xpsc_i2s_workdata->pm[0],
+ PSC_SEL(au1xpsc_i2s_workdata));
+ au_sync();
+
+ return 0;
+}
+
+struct snd_soc_dai au1xpsc_i2s_dai = {
+ .name = "au1xpsc_i2s",
+ .type = SND_SOC_DAI_I2S,
+ .probe = au1xpsc_i2s_probe,
+ .remove = au1xpsc_i2s_remove,
+ .suspend = au1xpsc_i2s_suspend,
+ .resume = au1xpsc_i2s_resume,
+ .playback = {
+ .rates = AU1XPSC_I2S_RATES,
+ .formats = AU1XPSC_I2S_FMTS,
+ .channels_min = 2,
+ .channels_max = 8, /* 2 without external help */
+ },
+ .capture = {
+ .rates = AU1XPSC_I2S_RATES,
+ .formats = AU1XPSC_I2S_FMTS,
+ .channels_min = 2,
+ .channels_max = 8, /* 2 without external help */
+ },
+ .ops = {
+ .trigger = au1xpsc_i2s_trigger,
+ .hw_params = au1xpsc_i2s_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = au1xpsc_i2s_set_fmt,
+ },
+};
+EXPORT_SYMBOL(au1xpsc_i2s_dai);
+
+static int __init au1xpsc_i2s_init(void)
+{
+ au1xpsc_i2s_workdata = NULL;
+ return 0;
+}
+
+static void __exit au1xpsc_i2s_exit(void)
+{
+}
+
+module_init(au1xpsc_i2s_init);
+module_exit(au1xpsc_i2s_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
new file mode 100644
index 00000000000..8fdb1a04a07
--- /dev/null
+++ b/sound/soc/au1x/psc.h
@@ -0,0 +1,53 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ */
+
+#ifndef _AU1X_PCM_H
+#define _AU1X_PCM_H
+
+extern struct snd_soc_dai au1xpsc_ac97_dai;
+extern struct snd_soc_dai au1xpsc_i2s_dai;
+extern struct snd_soc_platform au1xpsc_soc_platform;
+extern struct snd_ac97_bus_ops soc_ac97_ops;
+
+struct au1xpsc_audio_data {
+ void __iomem *mmio;
+
+ unsigned long cfg;
+ unsigned long rate;
+
+ unsigned long pm[2];
+ struct resource *ioarea;
+};
+
+#define PCM_TX 0
+#define PCM_RX 1
+
+#define SUBSTREAM_TYPE(substream) \
+ ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
+
+/* easy access macros */
+#define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
+#define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
+#define I2S_STAT(x) ((unsigned long)((x)->mmio) + PSC_I2SSTAT_OFFSET)
+#define I2S_CFG(x) ((unsigned long)((x)->mmio) + PSC_I2SCFG_OFFSET)
+#define I2S_PCR(x) ((unsigned long)((x)->mmio) + PSC_I2SPCR_OFFSET)
+#define AC97_CFG(x) ((unsigned long)((x)->mmio) + PSC_AC97CFG_OFFSET)
+#define AC97_CDC(x) ((unsigned long)((x)->mmio) + PSC_AC97CDC_OFFSET)
+#define AC97_EVNT(x) ((unsigned long)((x)->mmio) + PSC_AC97EVNT_OFFSET)
+#define AC97_PCR(x) ((unsigned long)((x)->mmio) + PSC_AC97PCR_OFFSET)
+#define AC97_RST(x) ((unsigned long)((x)->mmio) + PSC_AC97RST_OFFSET)
+#define AC97_STAT(x) ((unsigned long)((x)->mmio) + PSC_AC97STAT_OFFSET)
+
+#endif
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c
new file mode 100644
index 00000000000..f75ae7f62c3
--- /dev/null
+++ b/sound/soc/au1x/sample-ac97.c
@@ -0,0 +1,144 @@
+/*
+ * Sample Au12x0/Au1550 PSC AC97 sound machine.
+ *
+ * Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms outlined in the file COPYING at the root of this
+ * source archive.
+ *
+ * This is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at PSC1 (e.g. DB1200 demoboards).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+
+#include "../codecs/ac97.h"
+#include "psc.h"
+
+static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &au1xpsc_ac97_dai, /* see psc-ac97.c */
+ .codec_dai = &ac97_dai, /* see codecs/ac97.c */
+ .init = au1xpsc_sample_ac97_init,
+ .ops = NULL,
+};
+
+static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+ .name = "Au1xxx PSC AC97 Audio",
+ .dai_link = &au1xpsc_sample_ac97_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
+ .machine = &au1xpsc_sample_ac97_machine,
+ .platform = &au1xpsc_soc_platform, /* see dbdma2.c */
+ .codec_dev = &soc_codec_dev_ac97,
+};
+
+static struct resource au1xpsc_psc1_res[] = {
+ [0] = {
+ .start = CPHYSADDR(PSC1_BASE_ADDR),
+ .end = CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+#ifdef CONFIG_SOC_AU1200
+ .start = AU1200_PSC1_INT,
+ .end = AU1200_PSC1_INT,
+#elif defined(CONFIG_SOC_AU1550)
+ .start = AU1550_PSC1_INT,
+ .end = AU1550_PSC1_INT,
+#endif
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = DSCR_CMD0_PSC1_TX,
+ .end = DSCR_CMD0_PSC1_TX,
+ .flags = IORESOURCE_DMA,
+ },
+ [3] = {
+ .start = DSCR_CMD0_PSC1_RX,
+ .end = DSCR_CMD0_PSC1_RX,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct platform_device *au1xpsc_sample_ac97_dev;
+
+static int __init au1xpsc_sample_ac97_load(void)
+{
+ int ret;
+
+#ifdef CONFIG_SOC_AU1200
+ unsigned long io;
+
+ /* modify sys_pinfunc for AC97 on PSC1 */
+ io = au_readl(SYS_PINFUNC);
+ io |= SYS_PINFUNC_P1C;
+ io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B);
+ au_writel(io, SYS_PINFUNC);
+ au_sync();
+#endif
+
+ ret = -ENOMEM;
+
+ /* setup PSC clock source for AC97 part: external clock provided
+ * by codec. The psc-ac97.c driver depends on this setting!
+ */
+ au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET);
+ au_sync();
+
+ au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1);
+ if (!au1xpsc_sample_ac97_dev)
+ goto out;
+
+ au1xpsc_sample_ac97_dev->resource =
+ kmemdup(au1xpsc_psc1_res, sizeof(struct resource) *
+ ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL);
+ au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res);
+ au1xpsc_sample_ac97_dev->id = 1;
+
+ platform_set_drvdata(au1xpsc_sample_ac97_dev,
+ &au1xpsc_sample_ac97_devdata);
+ au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev;
+ ret = platform_device_add(au1xpsc_sample_ac97_dev);
+
+ if (ret) {
+ platform_device_put(au1xpsc_sample_ac97_dev);
+ au1xpsc_sample_ac97_dev = NULL;
+ }
+
+out:
+ return ret;
+}
+
+static void __exit au1xpsc_sample_ac97_exit(void)
+{
+ platform_device_unregister(au1xpsc_sample_ac97_dev);
+}
+
+module_init(au1xpsc_sample_ac97_load);
+module_exit(au1xpsc_sample_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
new file mode 100644
index 00000000000..dc006206f62
--- /dev/null
+++ b/sound/soc/blackfin/Kconfig
@@ -0,0 +1,101 @@
+config SND_BF5XX_I2S
+ tristate "SoC I2S Audio for the ADI BF5xx chip"
+ depends on BLACKFIN && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Blackfin SPORT (synchronous serial ports) interface in I2S
+ mode (supports single stereo In/Out).
+ You will also need to select the audio interfaces to support below.
+
+config SND_BF5XX_SOC_SSM2602
+ tristate "SoC SSM2602 Audio support for BF52x ezkit"
+ depends on SND_BF5XX_I2S
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_SSM2602
+ select I2C
+ select I2C_BLACKFIN_TWI
+ help
+ Say Y if you want to add support for SoC audio on BF527-EZKIT.
+
+config SND_BF5XX_SOC_AD73311
+ tristate "SoC AD73311 Audio support for Blackfin"
+ depends on SND_BF5XX_I2S
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_AD73311
+ help
+ Say Y if you want to add support for AD73311 codec on Blackfin.
+
+config SND_BFIN_AD73311_SE
+ int "PF pin for AD73311L Chip Select"
+ depends on SND_BF5XX_SOC_AD73311
+ default 4
+ help
+ Enter the GPIO used to control AD73311's SE pin. Acceptable
+ values are 0 to 7
+
+config SND_BF5XX_AC97
+ tristate "SoC AC97 Audio for the ADI BF5xx chip"
+ depends on BLACKFIN && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Blackfin SPORT (synchronous serial ports) interface in slot 16
+ mode (pseudo AC97 interface).
+ You will also need to select the audio interfaces to support below.
+
+ Note:
+ AC97 codecs which do not implment the slot-16 mode will not function
+ properly with this driver. This driver is known to work with the
+ Analog Devices line of AC97 codecs.
+
+config SND_MMAP_SUPPORT
+ bool "Enable MMAP Support"
+ depends on SND_BF5XX_AC97
+ default y
+ help
+ Say y if you want AC97 driver to support mmap mode.
+ We introduce an intermediate buffer to simulate mmap.
+
+config SND_BF5XX_SOC_SPORT
+ tristate
+
+config SND_BF5XX_SOC_I2S
+ tristate
+ select SND_BF5XX_SOC_SPORT
+
+config SND_BF5XX_SOC_AC97
+ tristate
+ select AC97_BUS
+ select SND_SOC_AC97_BUS
+ select SND_BF5XX_SOC_SPORT
+
+config SND_BF5XX_SOC_AD1980
+ tristate "SoC AD1980/1 Audio support for BF5xx"
+ depends on SND_BF5XX_AC97
+ select SND_BF5XX_SOC_AC97
+ select SND_SOC_AD1980
+ help
+ Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+
+config SND_BF5XX_SPORT_NUM
+ int "Set a SPORT for Sound chip"
+ depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
+ range 0 3 if BF54x
+ range 0 1 if (BF53x || BF561)
+ default 0
+ help
+ Set the correct SPORT for sound chip.
+
+config SND_BF5XX_HAVE_COLD_RESET
+ bool "BOARD has COLD Reset GPIO"
+ depends on SND_BF5XX_AC97
+ default y if BFIN548_EZKIT
+ default n if !BFIN548_EZKIT
+
+config SND_BF5XX_RESET_GPIO_NUM
+ int "Set a GPIO for cold reset"
+ depends on SND_BF5XX_HAVE_COLD_RESET
+ range 0 159
+ default 19 if BFIN548_EZKIT
+ default 5 if BFIN537_STAMP
+ help
+ Set the correct GPIO for RESET the sound chip.
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
new file mode 100644
index 00000000000..97bb37a6359
--- /dev/null
+++ b/sound/soc/blackfin/Makefile
@@ -0,0 +1,21 @@
+# Blackfin Platform Support
+snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
+snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
+snd-soc-bf5xx-sport-objs := bf5xx-sport.o
+snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
+snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
+
+obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
+obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
+obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
+obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
+
+# Blackfin Machine Support
+snd-ad1980-objs := bf5xx-ad1980.o
+snd-ssm2602-objs := bf5xx-ssm2602.o
+snd-ad73311-objs := bf5xx-ad73311.o
+
+obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
+obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
+obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
new file mode 100644
index 00000000000..25e50d2ea1e
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -0,0 +1,457 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ac97-pcm.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ * Description: DMA Driver for AC97 sound chip
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "bf5xx-ac97-pcm.h"
+#include "bf5xx-ac97.h"
+#include "bf5xx-sport.h"
+
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bf5xx_pcm_to_ac97(
+ (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
+ (__u32 *)runtime->dma_area + sport->tx_pos, count);
+ sport->tx_pos += runtime->period_size;
+ if (sport->tx_pos >= runtime->buffer_size)
+ sport->tx_pos %= runtime->buffer_size;
+ sport->tx_delay_pos = sport->tx_pos;
+ } else {
+ bf5xx_ac97_to_pcm(
+ (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
+ (__u32 *)runtime->dma_area + sport->rx_pos, count);
+ sport->rx_pos += runtime->period_size;
+ if (sport->rx_pos >= runtime->buffer_size)
+ sport->rx_pos %= runtime->buffer_size;
+ }
+}
+#endif
+
+static void bf5xx_dma_irq(void *data)
+{
+ struct snd_pcm_substream *pcm = data;
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ struct sport_device *sport = runtime->private_data;
+ bf5xx_mmap_copy(pcm, runtime->period_size);
+ if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sport->once == 0) {
+ snd_pcm_period_elapsed(pcm);
+ bf5xx_mmap_copy(pcm, runtime->period_size);
+ sport->once = 1;
+ }
+ }
+#endif
+ snd_pcm_period_elapsed(pcm);
+}
+
+/* The memory size for pure pcm data is 128*1024 = 0x20000 bytes.
+ * The total rx/tx buffer is for ac97 frame to hold all pcm data
+ * is 0x20000 * sizeof(struct ac97_frame) / 4.
+ */
+#ifdef CONFIG_SND_MMAP_SUPPORT
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+#else
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+#endif
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 0x10000,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/32,
+ .buffer_bytes_max = 0x20000, /* 128 kbytes */
+ .fifo_size = 16,
+};
+
+static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max
+ * sizeof(struct ac97_frame) / 4;
+
+ snd_pcm_lib_malloc_pages(substream, size);
+
+ return 0;
+}
+
+static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ memset(runtime->dma_area, 0, runtime->buffer_size);
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+
+ /* An intermediate buffer is introduced for implementing mmap for
+ * SPORT working in TMD mode(include AC97).
+ */
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
+ runtime->period_size * sizeof(struct ac97_frame));
+ } else {
+ sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
+ runtime->period_size * sizeof(struct ac97_frame));
+ }
+#else
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_tx_dma(sport, runtime->dma_area, runtime->periods,
+ runtime->period_size * sizeof(struct ac97_frame));
+ } else {
+ sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_rx_dma(sport, runtime->dma_area, runtime->periods,
+ runtime->period_size * sizeof(struct ac97_frame));
+ }
+#endif
+ return 0;
+}
+
+static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ int ret = 0;
+
+ pr_debug("%s enter\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bf5xx_mmap_copy(substream, runtime->period_size);
+ snd_pcm_period_elapsed(substream);
+ sport->tx_delay_pos = 0;
+ sport_tx_start(sport);
+ }
+ else
+ sport_rx_start(sport);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ sport->tx_pos = 0;
+#endif
+ sport_tx_stop(sport);
+ } else {
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ sport->rx_pos = 0;
+#endif
+ sport_rx_stop(sport);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ unsigned int curr;
+
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ curr = sport->tx_delay_pos;
+ else
+ curr = sport->rx_pos;
+#else
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame);
+ else
+ curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame);
+
+#endif
+ return curr;
+}
+
+static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+ snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ if (sport_handle != NULL)
+ runtime->private_data = sport_handle;
+ else {
+ pr_err("sport_handle is NULL\n");
+ return -1;
+ }
+ return 0;
+
+ out:
+ return ret;
+}
+
+static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+
+ pr_debug("%s enter\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport->once = 0;
+ memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
+ } else
+ memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_MMAP_SUPPORT
+static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ size_t size = vma->vm_end - vma->vm_start;
+ vma->vm_start = (unsigned long)runtime->dma_area;
+ vma->vm_end = vma->vm_start + size;
+ vma->vm_flags |= VM_SHARED;
+ return 0 ;
+}
+#else
+static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos,
+ void __user *buf, snd_pcm_uframes_t count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ pr_debug("%s copy pos:0x%lx count:0x%lx\n",
+ substream->stream ? "Capture" : "Playback", pos, count);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ bf5xx_pcm_to_ac97(
+ (struct ac97_frame *)runtime->dma_area + pos,
+ buf, count);
+ else
+ bf5xx_ac97_to_pcm(
+ (struct ac97_frame *)runtime->dma_area + pos,
+ buf, count);
+ return 0;
+}
+#endif
+
+struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
+ .open = bf5xx_pcm_open,
+ .close = bf5xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = bf5xx_pcm_hw_params,
+ .hw_free = bf5xx_pcm_hw_free,
+ .prepare = bf5xx_pcm_prepare,
+ .trigger = bf5xx_pcm_trigger,
+ .pointer = bf5xx_pcm_pointer,
+#ifdef CONFIG_SND_MMAP_SUPPORT
+ .mmap = bf5xx_pcm_mmap,
+#else
+ .copy = bf5xx_pcm_copy,
+#endif
+};
+
+static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max
+ * sizeof(struct ac97_frame) / 4;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area) {
+ pr_err("Failed to allocate dma memory\n");
+ pr_err("Please increase uncached DMA memory region\n");
+ return -ENOMEM;
+ }
+ buf->bytes = size;
+
+ pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
+ buf->area, buf->bytes);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
+/*
+ * Need to allocate local buffer when enable
+ * MMAP for SPORT working in TMD mode (include AC97).
+ */
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!sport_handle->tx_dma_buf) {
+ sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
+ size, &sport_handle->tx_dma_phy, GFP_KERNEL);
+ if (!sport_handle->tx_dma_buf) {
+ pr_err("Failed to allocate memory for tx dma \
+ buf - Please increase uncached DMA \
+ memory region\n");
+ return -ENOMEM;
+ } else
+ memset(sport_handle->tx_dma_buf, 0, size);
+ } else
+ memset(sport_handle->tx_dma_buf, 0, size);
+ } else {
+ if (!sport_handle->rx_dma_buf) {
+ sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
+ size, &sport_handle->rx_dma_phy, GFP_KERNEL);
+ if (!sport_handle->rx_dma_buf) {
+ pr_err("Failed to allocate memory for rx dma \
+ buf - Please increase uncached DMA \
+ memory region\n");
+ return -ENOMEM;
+ } else
+ memset(sport_handle->rx_dma_buf, 0, size);
+ } else
+ memset(sport_handle->rx_dma_buf, 0, size);
+ }
+#endif
+ return 0;
+}
+
+static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
+ sizeof(struct ac97_frame) / 4;
+#endif
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_coherent(NULL, buf->bytes, buf->area, 0);
+ buf->area = NULL;
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sport_handle->tx_dma_buf)
+ dma_free_coherent(NULL, size, \
+ sport_handle->tx_dma_buf, 0);
+ sport_handle->tx_dma_buf = NULL;
+ } else {
+
+ if (sport_handle->rx_dma_buf)
+ dma_free_coherent(NULL, size, \
+ sport_handle->rx_dma_buf, 0);
+ sport_handle->rx_dma_buf = NULL;
+ }
+#endif
+ }
+ if (sport_handle)
+ sport_done(sport_handle);
+}
+
+static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
+
+int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ pr_debug("%s enter\n", __func__);
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &bf5xx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+ if (dai->playback.channels_min) {
+ ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform bf5xx_ac97_soc_platform = {
+ .name = "bf5xx-audio",
+ .pcm_ops = &bf5xx_pcm_ac97_ops,
+ .pcm_new = bf5xx_pcm_ac97_new,
+ .pcm_free = bf5xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h
new file mode 100644
index 00000000000..350125a0ae2
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.h
@@ -0,0 +1,29 @@
+/*
+ * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin
+ *
+ * Copyright 2007 Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_AC97_PCM_H
+#define _BF5XX_AC97_PCM_H
+
+struct bf5xx_pcm_dma_params {
+ char *name; /* stream identifier */
+};
+
+struct bf5xx_gpio {
+ u32 sys;
+ u32 rx;
+ u32 tx;
+ u32 clk;
+ u32 frm;
+};
+
+/* platform data */
+extern struct snd_soc_platform bf5xx_ac97_soc_platform;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
new file mode 100644
index 00000000000..5e5aafb6485
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -0,0 +1,406 @@
+/*
+ * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip.
+ *
+ * Author: Roy Huang
+ * Created: 11th. June 2007
+ * Copyright: Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/portmux.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include "bf5xx-sport.h"
+#include "bf5xx-ac97.h"
+
+#if defined(CONFIG_BF54x)
+#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \
+ P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
+
+#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \
+ P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
+
+#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \
+ P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0}
+
+#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \
+ P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0}
+#else
+#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
+ P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
+
+#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
+ P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
+#endif
+
+static int *cmd_count;
+static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
+
+#if defined(CONFIG_BF54x)
+static struct sport_param sport_params[4] = {
+ {
+ .dma_rx_chan = CH_SPORT0_RX,
+ .dma_tx_chan = CH_SPORT0_TX,
+ .err_irq = IRQ_SPORT0_ERR,
+ .regs = (struct sport_register *)SPORT0_TCR1,
+ },
+ {
+ .dma_rx_chan = CH_SPORT1_RX,
+ .dma_tx_chan = CH_SPORT1_TX,
+ .err_irq = IRQ_SPORT1_ERR,
+ .regs = (struct sport_register *)SPORT1_TCR1,
+ },
+ {
+ .dma_rx_chan = CH_SPORT2_RX,
+ .dma_tx_chan = CH_SPORT2_TX,
+ .err_irq = IRQ_SPORT2_ERR,
+ .regs = (struct sport_register *)SPORT2_TCR1,
+ },
+ {
+ .dma_rx_chan = CH_SPORT3_RX,
+ .dma_tx_chan = CH_SPORT3_TX,
+ .err_irq = IRQ_SPORT3_ERR,
+ .regs = (struct sport_register *)SPORT3_TCR1,
+ }
+};
+#else
+static struct sport_param sport_params[2] = {
+ {
+ .dma_rx_chan = CH_SPORT0_RX,
+ .dma_tx_chan = CH_SPORT0_TX,
+ .err_irq = IRQ_SPORT0_ERROR,
+ .regs = (struct sport_register *)SPORT0_TCR1,
+ },
+ {
+ .dma_rx_chan = CH_SPORT1_RX,
+ .dma_tx_chan = CH_SPORT1_TX,
+ .err_irq = IRQ_SPORT1_ERROR,
+ .regs = (struct sport_register *)SPORT1_TCR1,
+ }
+};
+#endif
+
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
+ size_t count)
+{
+ while (count--) {
+ dst->ac97_tag = TAG_VALID | TAG_PCM;
+ (dst++)->ac97_pcm = *src++;
+ }
+}
+EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
+
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+ size_t count)
+{
+ while (count--)
+ *(dst++) = (src++)->ac97_pcm;
+}
+EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
+
+static unsigned int sport_tx_curr_frag(struct sport_device *sport)
+{
+ return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
+ sport->tx_fragsize;
+}
+
+static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
+{
+ struct sport_device *sport = sport_handle;
+ int nextfrag = sport_tx_curr_frag(sport);
+ struct ac97_frame *nextwrite;
+
+ sport_incfrag(sport, &nextfrag, 1);
+
+ nextwrite = (struct ac97_frame *)(sport->tx_buf + \
+ nextfrag * sport->tx_fragsize);
+ pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
+ sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
+ nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
+ nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
+ nextwrite[cmd_count[nextfrag]].ac97_data = data;
+ ++cmd_count[nextfrag];
+ pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
+ addr >> 8, data, nextfrag);
+}
+
+static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ac97_frame out_frame[2], in_frame[2];
+
+ pr_debug("%s enter 0x%x\n", __func__, reg);
+
+ /* When dma descriptor is enabled, the register should not be read */
+ if (sport_handle->tx_run || sport_handle->rx_run) {
+ pr_err("Could you send a mail to cliff.cai@analog.com "
+ "to report this?\n");
+ return -EFAULT;
+ }
+
+ memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
+ memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
+ out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
+ out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
+ sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
+ (unsigned char *)&in_frame,
+ 2 * sizeof(struct ac97_frame));
+ return in_frame[1].ac97_data;
+}
+
+void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
+
+ if (sport_handle->tx_run) {
+ enqueue_cmd(ac97, (reg << 8), val); /* write */
+ enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
+ } else {
+ struct ac97_frame frame;
+ memset(&frame, 0, sizeof(struct ac97_frame));
+ frame.ac97_tag = TAG_VALID | TAG_CMD;
+ frame.ac97_addr = (reg << 8);
+ frame.ac97_data = val;
+ sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
+ NULL, sizeof(struct ac97_frame));
+ }
+}
+
+static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
+ (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
+
+#define CONCAT(a, b, c) a ## b ## c
+#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
+
+ u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
+ u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
+
+ pr_debug("%s enter\n", __func__);
+
+ peripheral_free(per);
+ gpio_request(gpio, "bf5xx-ac97");
+ gpio_direction_output(gpio, 1);
+ udelay(2);
+ gpio_set_value(gpio, 0);
+ udelay(1);
+ gpio_free(gpio);
+ peripheral_request(per, "soc-audio");
+#else
+ pr_info("%s: Not implemented\n", __func__);
+#endif
+}
+
+static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ pr_debug("%s enter\n", __func__);
+
+ /* It is specified for bf548-ezkit */
+ gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
+ /* Keep reset pin low for 1 ms */
+ mdelay(1);
+ gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
+ /* Wait for bit clock recover */
+ mdelay(1);
+#else
+ pr_info("%s: Not implemented\n", __func__);
+#endif
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = bf5xx_ac97_read,
+ .write = bf5xx_ac97_write,
+ .warm_reset = bf5xx_ac97_warm_reset,
+ .reset = bf5xx_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+#ifdef CONFIG_PM
+static int bf5xx_ac97_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct sport_device *sport =
+ (struct sport_device *)dai->private_data;
+
+ pr_debug("%s : sport %d\n", __func__, dai->id);
+ if (!dai->active)
+ return 0;
+ if (dai->capture.active)
+ sport_rx_stop(sport);
+ if (dai->playback.active)
+ sport_tx_stop(sport);
+ return 0;
+}
+
+static int bf5xx_ac97_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct sport_device *sport =
+ (struct sport_device *)dai->private_data;
+
+ pr_debug("%s : sport %d\n", __func__, dai->id);
+ if (!dai->active)
+ return 0;
+
+ ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ if (dai->capture.active)
+ sport_rx_start(sport);
+ if (dai->playback.active)
+ sport_tx_start(sport);
+ return 0;
+}
+
+#else
+#define bf5xx_ac97_suspend NULL
+#define bf5xx_ac97_resume NULL
+#endif
+
+static int bf5xx_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+#if defined(CONFIG_BF54x)
+ u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
+ PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
+#else
+ u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
+#endif
+ cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
+ if (cmd_count == NULL)
+ return -ENOMEM;
+
+ if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+ pr_err("Requesting Peripherals failed\n");
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ /* Request PB3 as reset pin */
+ if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
+ pr_err("Failed to request GPIO_%d for reset\n",
+ CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+ peripheral_free_list(&sport_req[sport_num][0]);
+ return -1;
+ }
+ gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
+#endif
+ sport_handle = sport_init(&sport_params[sport_num], 2, \
+ sizeof(struct ac97_frame), NULL);
+ if (!sport_handle) {
+ peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+ return -ENODEV;
+ }
+ /*SPORT works in TDM mode to simulate AC97 transfers*/
+ ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ kfree(sport_handle);
+ peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+ return -EBUSY;
+ }
+
+ ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ kfree(sport_handle);
+ peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+ return -EBUSY;
+ }
+
+ ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ kfree(sport_handle);
+ peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void bf5xx_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ free_page((unsigned long)cmd_count);
+ cmd_count = NULL;
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+}
+
+struct snd_soc_dai bfin_ac97_dai = {
+ .name = "bf5xx-ac97",
+ .id = 0,
+ .type = SND_SOC_DAI_AC97,
+ .probe = bf5xx_ac97_probe,
+ .remove = bf5xx_ac97_remove,
+ .suspend = bf5xx_ac97_suspend,
+ .resume = bf5xx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+EXPORT_SYMBOL_GPL(bfin_ac97_dai);
+
+MODULE_AUTHOR("Roy Huang");
+MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h
new file mode 100644
index 00000000000..3f77cc558dc
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.h
@@ -0,0 +1,36 @@
+/*
+ * linux/sound/arm/bf5xx-ac97.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_AC97_H
+#define _BF5XX_AC97_H
+
+extern struct snd_ac97_bus_ops bf5xx_ac97_ops;
+extern struct snd_ac97 *ac97;
+/* Frame format in memory, only support stereo currently */
+struct ac97_frame {
+ u16 ac97_tag; /* slot 0 */
+ u16 ac97_addr; /* slot 1 */
+ u16 ac97_data; /* slot 2 */
+ u32 ac97_pcm; /* slot 3 and 4: left and right pcm data */
+} __attribute__ ((packed));
+
+#define TAG_VALID 0x8000
+#define TAG_CMD 0x6000
+#define TAG_PCM_LEFT 0x1000
+#define TAG_PCM_RIGHT 0x0800
+#define TAG_PCM (TAG_PCM_LEFT | TAG_PCM_RIGHT)
+
+extern struct snd_soc_dai bfin_ac97_dai;
+
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
+ size_t count);
+
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+ size_t count);
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
new file mode 100644
index 00000000000..124425d2232
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -0,0 +1,113 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ad1980.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ * Description: Board driver for AD1980/1 audio codec
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <linux/gpio.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad1980.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-ac97-pcm.h"
+#include "bf5xx-ac97.h"
+
+static struct snd_soc_machine bf5xx_board;
+
+static int bf5xx_board_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ pr_debug("%s enter\n", __func__);
+ cpu_dai->private_data = sport_handle;
+ return 0;
+}
+
+static struct snd_soc_ops bf5xx_board_ops = {
+ .startup = bf5xx_board_startup,
+};
+
+static struct snd_soc_dai_link bf5xx_board_dai = {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &bfin_ac97_dai,
+ .codec_dai = &ad1980_dai,
+ .ops = &bf5xx_board_ops,
+};
+
+static struct snd_soc_machine bf5xx_board = {
+ .name = "bf5xx-board",
+ .dai_link = &bf5xx_board_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_board_snd_devdata = {
+ .machine = &bf5xx_board,
+ .platform = &bf5xx_ac97_soc_platform,
+ .codec_dev = &soc_codec_dev_ad1980,
+};
+
+static struct platform_device *bf5xx_board_snd_device;
+
+static int __init bf5xx_board_init(void)
+{
+ int ret;
+
+ bf5xx_board_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bf5xx_board_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata);
+ bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev;
+ ret = platform_device_add(bf5xx_board_snd_device);
+
+ if (ret)
+ platform_device_put(bf5xx_board_snd_device);
+
+ return ret;
+}
+
+static void __exit bf5xx_board_exit(void)
+{
+ platform_device_unregister(bf5xx_board_snd_device);
+}
+
+module_init(bf5xx_board_init);
+module_exit(bf5xx_board_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
new file mode 100644
index 00000000000..622c9b90953
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -0,0 +1,240 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ad73311.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Thur Sep 25 2008
+ * Description: Board driver for ad73311 sound chip
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad73311.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+#if CONFIG_SND_BF5XX_SPORT_NUM == 0
+#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1
+#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1
+#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2
+#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16
+#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT
+#else
+#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1
+#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1
+#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2
+#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16
+#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT
+#endif
+
+#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
+
+static struct snd_soc_machine bf5xx_ad73311;
+
+static int snd_ad73311_startup(void)
+{
+ pr_debug("%s enter\n", __func__);
+
+ /* Pull up SE pin on AD73311L */
+ gpio_set_value(GPIO_SE, 1);
+ return 0;
+}
+
+static int snd_ad73311_configure(void)
+{
+ unsigned short ctrl_regs[6];
+ unsigned short status = 0;
+ int count = 0;
+
+ /* DMCLK = MCLK = 16.384 MHz
+ * SCLK = DMCLK/8 = 2.048 MHz
+ * Sample Rate = DMCLK/2048 = 8 KHz
+ */
+ ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \
+ REGB_SCDIV(0) | REGB_DIRATE(0);
+ ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \
+ REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ;
+ ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \
+ REGD_IGS(2);
+ ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f);
+ ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ;
+ ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA;
+
+ local_irq_disable();
+ snd_ad73311_startup();
+ udelay(1);
+
+ bfin_write_SPORT_TCR1(TFSR);
+ bfin_write_SPORT_TCR2(0xF);
+ SSYNC();
+
+ /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to
+ * FIFO before enable SPORT to transfer the data
+ */
+ for (count = 0; count < 6; count++)
+ bfin_write_SPORT_TX16(ctrl_regs[count]);
+ SSYNC();
+ bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN);
+ SSYNC();
+
+ /* When TUVF is set, the data is already send out */
+ while (!(status & TUVF) && count++ < 10000) {
+ udelay(1);
+ status = bfin_read_SPORT_STAT();
+ SSYNC();
+ }
+ bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN);
+ SSYNC();
+ local_irq_enable();
+
+ if (count == 10000) {
+ printk(KERN_ERR "ad73311: failed to configure codec\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int bf5xx_probe(struct platform_device *pdev)
+{
+ int err;
+ if (gpio_request(GPIO_SE, "AD73311_SE")) {
+ printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE);
+ return -EBUSY;
+ }
+
+ gpio_direction_output(GPIO_SE, 0);
+
+ err = snd_ad73311_configure();
+ if (err < 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ pr_debug("%s enter\n", __func__);
+ cpu_dai->private_data = sport_handle;
+ return 0;
+}
+
+static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
+ params_format(params));
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+
+static struct snd_soc_ops bf5xx_ad73311_ops = {
+ .startup = bf5xx_ad73311_startup,
+ .hw_params = bf5xx_ad73311_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ad73311_dai = {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai = &bf5xx_i2s_dai,
+ .codec_dai = &ad73311_dai,
+ .ops = &bf5xx_ad73311_ops,
+};
+
+static struct snd_soc_machine bf5xx_ad73311 = {
+ .name = "bf5xx_ad73311",
+ .probe = bf5xx_probe,
+ .dai_link = &bf5xx_ad73311_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
+ .machine = &bf5xx_ad73311,
+ .platform = &bf5xx_i2s_soc_platform,
+ .codec_dev = &soc_codec_dev_ad73311,
+};
+
+static struct platform_device *bf52x_ad73311_snd_device;
+
+static int __init bf5xx_ad73311_init(void)
+{
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+ bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bf52x_ad73311_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
+ bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
+ ret = platform_device_add(bf52x_ad73311_snd_device);
+
+ if (ret)
+ platform_device_put(bf52x_ad73311_snd_device);
+
+ return ret;
+}
+
+static void __exit bf5xx_ad73311_exit(void)
+{
+ pr_debug("%s enter\n", __func__);
+ platform_device_unregister(bf52x_ad73311_snd_device);
+}
+
+module_init(bf5xx_ad73311_init);
+module_exit(bf5xx_ad73311_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
new file mode 100644
index 00000000000..61fccf92519
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -0,0 +1,288 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-i2s-pcm.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ * Description: DMA driver for i2s codec
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+#include "bf5xx-sport.h"
+
+static void bf5xx_dma_irq(void *data)
+{
+ struct snd_pcm_substream *pcm = data;
+ snd_pcm_period_elapsed(pcm);
+}
+
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 0x10000,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/32,
+ .buffer_bytes_max = 0x20000, /* 128 kbytes */
+ .fifo_size = 16,
+};
+
+static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+ snd_pcm_lib_malloc_pages(substream, size);
+
+ return 0;
+}
+
+static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ int period_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+ pr_debug("%s enter\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_tx_dma(sport, runtime->dma_area,
+ runtime->periods, period_bytes);
+ } else {
+ sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_rx_dma(sport, runtime->dma_area,
+ runtime->periods, period_bytes);
+ }
+
+ return 0;
+}
+
+static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ int ret = 0;
+
+ pr_debug("%s enter\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_tx_start(sport);
+ else
+ sport_rx_start(sport);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_tx_stop(sport);
+ else
+ sport_rx_stop(sport);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ unsigned int diff;
+ snd_pcm_uframes_t frames;
+ pr_debug("%s enter\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ diff = sport_curr_offset_tx(sport);
+ frames = bytes_to_frames(substream->runtime, diff);
+ } else {
+ diff = sport_curr_offset_rx(sport);
+ frames = bytes_to_frames(substream->runtime, diff);
+ }
+ return frames;
+}
+
+static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+ snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime, \
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ if (sport_handle != NULL)
+ runtime->private_data = sport_handle;
+ else {
+ pr_err("sport_handle is NULL\n");
+ return -1;
+ }
+ return 0;
+
+ out:
+ return ret;
+}
+
+static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ size_t size = vma->vm_end - vma->vm_start;
+ vma->vm_start = (unsigned long)runtime->dma_area;
+ vma->vm_end = vma->vm_start + size;
+ vma->vm_flags |= VM_SHARED;
+
+ return 0 ;
+}
+
+struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
+ .open = bf5xx_pcm_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = bf5xx_pcm_hw_params,
+ .hw_free = bf5xx_pcm_hw_free,
+ .prepare = bf5xx_pcm_prepare,
+ .trigger = bf5xx_pcm_trigger,
+ .pointer = bf5xx_pcm_pointer,
+ .mmap = bf5xx_pcm_mmap,
+};
+
+static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area) {
+ pr_err("Failed to allocate dma memory \
+ Please increase uncached DMA memory region\n");
+ return -ENOMEM;
+ }
+ buf->bytes = size;
+
+ pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
+ buf->area, buf->bytes);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
+ return 0;
+}
+
+static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_coherent(NULL, buf->bytes, buf->area, 0);
+ buf->area = NULL;
+ }
+ if (sport_handle)
+ sport_done(sport_handle);
+}
+
+static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
+
+int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ pr_debug("%s enter\n", __func__);
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &bf5xx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+ if (dai->playback.channels_min) {
+ ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform bf5xx_i2s_soc_platform = {
+ .name = "bf5xx-audio",
+ .pcm_ops = &bf5xx_pcm_i2s_ops,
+ .pcm_new = bf5xx_pcm_i2s_new,
+ .pcm_free = bf5xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h
new file mode 100644
index 00000000000..4d4609a97c5
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h
@@ -0,0 +1,29 @@
+/*
+ * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin
+ *
+ * Copyright 2007 Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_I2S_PCM_H
+#define _BF5XX_I2S_PCM_H
+
+struct bf5xx_pcm_dma_params {
+ char *name; /* stream identifier */
+};
+
+struct bf5xx_gpio {
+ u32 sys;
+ u32 rx;
+ u32 tx;
+ u32 clk;
+ u32 frm;
+};
+
+/* platform data */
+extern struct snd_soc_platform bf5xx_i2s_soc_platform;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
new file mode 100644
index 00000000000..827587f0818
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -0,0 +1,311 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-i2s.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ * Description: Blackfin I2S CPU DAI driver
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/portmux.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s.h"
+
+struct bf5xx_i2s_port {
+ u16 tcr1;
+ u16 rcr1;
+ u16 tcr2;
+ u16 rcr2;
+ int counter;
+};
+
+static struct bf5xx_i2s_port bf5xx_i2s;
+static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
+
+static struct sport_param sport_params[2] = {
+ {
+ .dma_rx_chan = CH_SPORT0_RX,
+ .dma_tx_chan = CH_SPORT0_TX,
+ .err_irq = IRQ_SPORT0_ERROR,
+ .regs = (struct sport_register *)SPORT0_TCR1,
+ },
+ {
+ .dma_rx_chan = CH_SPORT1_RX,
+ .dma_tx_chan = CH_SPORT1_TX,
+ .err_irq = IRQ_SPORT1_ERROR,
+ .regs = (struct sport_register *)SPORT1_TCR1,
+ }
+};
+
+static u16 sport_req[][7] = {
+ { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
+ P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0},
+ { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
+ P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0},
+};
+
+static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ int ret = 0;
+
+ /* interface format:support I2S,slave mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ bf5xx_i2s.tcr1 |= TFSR | TCKFE;
+ bf5xx_i2s.rcr1 |= RFSR | RCKFE;
+ bf5xx_i2s.tcr2 |= TSFSE;
+ bf5xx_i2s.rcr2 |= RSFSE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ bf5xx_i2s.tcr1 |= TFSR;
+ bf5xx_i2s.rcr1 |= RFSR;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s enter\n", __func__);
+
+ /*this counter is used for counting how many pcm streams are opened*/
+ bf5xx_i2s.counter++;
+ return 0;
+}
+
+static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+
+ bf5xx_i2s.tcr2 &= ~0x1f;
+ bf5xx_i2s.rcr2 &= ~0x1f;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bf5xx_i2s.tcr2 |= 15;
+ bf5xx_i2s.rcr2 |= 15;
+ sport_handle->wdsize = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bf5xx_i2s.tcr2 |= 23;
+ bf5xx_i2s.rcr2 |= 23;
+ sport_handle->wdsize = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bf5xx_i2s.tcr2 |= 31;
+ bf5xx_i2s.rcr2 |= 31;
+ sport_handle->wdsize = 4;
+ break;
+ }
+
+ if (bf5xx_i2s.counter == 1) {
+ /*
+ * TX and RX are not independent,they are enabled at the
+ * same time, even if only one side is running. So, we
+ * need to configure both of them at the time when the first
+ * stream is opened.
+ *
+ * CPU DAI:slave mode.
+ */
+ ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
+ bf5xx_i2s.rcr2, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
+ bf5xx_i2s.tcr2, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s enter\n", __func__);
+ bf5xx_i2s.counter--;
+}
+
+static int bf5xx_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s enter\n", __func__);
+ if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+ pr_err("Requesting Peripherals failed\n");
+ return -EFAULT;
+ }
+
+ /* request DMA for SPORT */
+ sport_handle = sport_init(&sport_params[sport_num], 4, \
+ 2 * sizeof(u32), NULL);
+ if (!sport_handle) {
+ peripheral_free_list(&sport_req[sport_num][0]);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void bf5xx_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s enter\n", __func__);
+ peripheral_free_list(&sport_req[sport_num][0]);
+}
+
+#ifdef CONFIG_PM
+static int bf5xx_i2s_suspend(struct platform_device *dev,
+ struct snd_soc_dai *dai)
+{
+ struct sport_device *sport =
+ (struct sport_device *)dai->private_data;
+
+ pr_debug("%s : sport %d\n", __func__, dai->id);
+ if (!dai->active)
+ return 0;
+ if (dai->capture.active)
+ sport_rx_stop(sport);
+ if (dai->playback.active)
+ sport_tx_stop(sport);
+ return 0;
+}
+
+static int bf5xx_i2s_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct sport_device *sport =
+ (struct sport_device *)dai->private_data;
+
+ pr_debug("%s : sport %d\n", __func__, dai->id);
+ if (!dai->active)
+ return 0;
+
+ ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ if (dai->capture.active)
+ sport_rx_start(sport);
+ if (dai->playback.active)
+ sport_tx_start(sport);
+ return 0;
+}
+
+#else
+#define bf5xx_i2s_suspend NULL
+#define bf5xx_i2s_resume NULL
+#endif
+
+#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai bf5xx_i2s_dai = {
+ .name = "bf5xx-i2s",
+ .id = 0,
+ .type = SND_SOC_DAI_I2S,
+ .probe = bf5xx_i2s_probe,
+ .remove = bf5xx_i2s_remove,
+ .suspend = bf5xx_i2s_suspend,
+ .resume = bf5xx_i2s_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = BF5XX_I2S_RATES,
+ .formats = BF5XX_I2S_FORMATS,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = BF5XX_I2S_RATES,
+ .formats = BF5XX_I2S_FORMATS,},
+ .ops = {
+ .startup = bf5xx_i2s_startup,
+ .shutdown = bf5xx_i2s_shutdown,
+ .hw_params = bf5xx_i2s_hw_params,},
+ .dai_ops = {
+ .set_fmt = bf5xx_i2s_set_dai_fmt,
+ },
+};
+EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-i2s.h b/sound/soc/blackfin/bf5xx-i2s.h
new file mode 100644
index 00000000000..7107d1a0b06
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.h
@@ -0,0 +1,14 @@
+/*
+ * linux/sound/arm/bf5xx-i2s.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_I2S_H
+#define _BF5XX_I2S_H
+
+extern struct snd_soc_dai bf5xx_i2s_dai;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
new file mode 100644
index 00000000000..3b99e484d55
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -0,0 +1,1032 @@
+/*
+ * File: bf5xx_sport.c
+ * Based on:
+ * Author: Roy Huang <roy.huang@analog.com>
+ *
+ * Created: Tue Sep 21 10:52:42 CEST 2004
+ * Description:
+ * Blackfin SPORT Driver
+ *
+ * Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
+#include <linux/bug.h>
+#include <asm/portmux.h>
+#include <asm/dma.h>
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+
+#include "bf5xx-sport.h"
+/* delay between frame sync pulse and first data bit in multichannel mode */
+#define FRAME_DELAY (1<<12)
+
+struct sport_device *sport_handle;
+EXPORT_SYMBOL(sport_handle);
+/* note: multichannel is in units of 8 channels,
+ * tdm_count is # channels NOT / 8 ! */
+int sport_set_multichannel(struct sport_device *sport,
+ int tdm_count, u32 mask, int packed)
+{
+ pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__,
+ tdm_count, mask, packed);
+
+ if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
+ return -EBUSY;
+
+ if (tdm_count & 0x7)
+ return -EINVAL;
+
+ if (tdm_count > 32)
+ return -EINVAL; /* Only support less than 32 channels now */
+
+ if (tdm_count) {
+ sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12;
+ sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
+ (packed ? (MCDTXPE|MCDRXPE) : 0);
+
+ sport->regs->mtcs0 = mask;
+ sport->regs->mrcs0 = mask;
+ sport->regs->mtcs1 = 0;
+ sport->regs->mrcs1 = 0;
+ sport->regs->mtcs2 = 0;
+ sport->regs->mrcs2 = 0;
+ sport->regs->mtcs3 = 0;
+ sport->regs->mrcs3 = 0;
+ } else {
+ sport->regs->mcmc1 = 0;
+ sport->regs->mcmc2 = 0;
+
+ sport->regs->mtcs0 = 0;
+ sport->regs->mrcs0 = 0;
+ }
+
+ sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0;
+ sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0;
+
+ SSYNC();
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_set_multichannel);
+
+int sport_config_rx(struct sport_device *sport, unsigned int rcr1,
+ unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv)
+{
+ if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
+ return -EBUSY;
+
+ sport->regs->rcr1 = rcr1;
+ sport->regs->rcr2 = rcr2;
+ sport->regs->rclkdiv = clkdiv;
+ sport->regs->rfsdiv = fsdiv;
+
+ SSYNC();
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_config_rx);
+
+int sport_config_tx(struct sport_device *sport, unsigned int tcr1,
+ unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv)
+{
+ if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
+ return -EBUSY;
+
+ sport->regs->tcr1 = tcr1;
+ sport->regs->tcr2 = tcr2;
+ sport->regs->tclkdiv = clkdiv;
+ sport->regs->tfsdiv = fsdiv;
+
+ SSYNC();
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_config_tx);
+
+static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
+ size_t fragsize, unsigned int cfg,
+ unsigned int x_count, unsigned int ycount, size_t wdsize)
+{
+
+ int i;
+
+ for (i = 0; i < fragcount; ++i) {
+ desc[i].next_desc_addr = (unsigned long)&(desc[i + 1]);
+ desc[i].start_addr = (unsigned long)buf + i*fragsize;
+ desc[i].cfg = cfg;
+ desc[i].x_count = x_count;
+ desc[i].x_modify = wdsize;
+ desc[i].y_count = ycount;
+ desc[i].y_modify = wdsize;
+ }
+
+ /* make circular */
+ desc[fragcount-1].next_desc_addr = (unsigned long)desc;
+
+ pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p,"
+ "next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
+ &(desc[0]), desc[0].next_desc_addr,
+ &(desc[1]), desc[1].next_desc_addr,
+ desc[0].x_count, desc[0].y_count,
+ desc[0].start_addr, desc[0].cfg);
+}
+
+static int sport_start(struct sport_device *sport)
+{
+ enable_dma(sport->dma_rx_chan);
+ enable_dma(sport->dma_tx_chan);
+ sport->regs->rcr1 |= RSPEN;
+ sport->regs->tcr1 |= TSPEN;
+ SSYNC();
+
+ return 0;
+}
+
+static int sport_stop(struct sport_device *sport)
+{
+ sport->regs->tcr1 &= ~TSPEN;
+ sport->regs->rcr1 &= ~RSPEN;
+ SSYNC();
+
+ disable_dma(sport->dma_rx_chan);
+ disable_dma(sport->dma_tx_chan);
+ return 0;
+}
+
+static inline int sport_hook_rx_dummy(struct sport_device *sport)
+{
+ struct dmasg *desc, temp_desc;
+ unsigned long flags;
+
+ BUG_ON(sport->dummy_rx_desc == NULL);
+ BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc);
+
+ /* Maybe the dummy buffer descriptor ring is damaged */
+ sport->dummy_rx_desc->next_desc_addr = \
+ (unsigned long)(sport->dummy_rx_desc+1);
+
+ local_irq_save(flags);
+ desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan);
+ /* Copy the descriptor which will be damaged to backup */
+ temp_desc = *desc;
+ desc->x_count = 0xa;
+ desc->y_count = 0;
+ desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc);
+ local_irq_restore(flags);
+ /* Waiting for dummy buffer descriptor is already hooked*/
+ while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
+ sizeof(struct dmasg)) !=
+ (unsigned long)sport->dummy_rx_desc)
+ ;
+ sport->curr_rx_desc = sport->dummy_rx_desc;
+ /* Restore the damaged descriptor */
+ *desc = temp_desc;
+
+ return 0;
+}
+
+static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
+{
+ if (dummy) {
+ sport->dummy_rx_desc->next_desc_addr = \
+ (unsigned long) sport->dummy_rx_desc;
+ sport->curr_rx_desc = sport->dummy_rx_desc;
+ } else
+ sport->curr_rx_desc = sport->dma_rx_desc;
+
+ set_dma_next_desc_addr(sport->dma_rx_chan, \
+ (unsigned long)(sport->curr_rx_desc));
+ set_dma_x_count(sport->dma_rx_chan, 0);
+ set_dma_x_modify(sport->dma_rx_chan, 0);
+ set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \
+ WDSIZE_32 | WNR));
+ set_dma_curr_addr(sport->dma_rx_chan, sport->curr_rx_desc->start_addr);
+ SSYNC();
+
+ return 0;
+}
+
+static inline int sport_tx_dma_start(struct sport_device *sport, int dummy)
+{
+ if (dummy) {
+ sport->dummy_tx_desc->next_desc_addr = \
+ (unsigned long) sport->dummy_tx_desc;
+ sport->curr_tx_desc = sport->dummy_tx_desc;
+ } else
+ sport->curr_tx_desc = sport->dma_tx_desc;
+
+ set_dma_next_desc_addr(sport->dma_tx_chan, \
+ (unsigned long)(sport->curr_tx_desc));
+ set_dma_x_count(sport->dma_tx_chan, 0);
+ set_dma_x_modify(sport->dma_tx_chan, 0);
+ set_dma_config(sport->dma_tx_chan,
+ (DMAFLOW_LARGE | NDSIZE_9 | WDSIZE_32));
+ set_dma_curr_addr(sport->dma_tx_chan, sport->curr_tx_desc->start_addr);
+ SSYNC();
+
+ return 0;
+}
+
+int sport_rx_start(struct sport_device *sport)
+{
+ unsigned long flags;
+ pr_debug("%s enter\n", __func__);
+ if (sport->rx_run)
+ return -EBUSY;
+ if (sport->tx_run) {
+ /* tx is running, rx is not running */
+ BUG_ON(sport->dma_rx_desc == NULL);
+ BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc);
+ local_irq_save(flags);
+ while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
+ sizeof(struct dmasg)) !=
+ (unsigned long)sport->dummy_rx_desc)
+ ;
+ sport->dummy_rx_desc->next_desc_addr =
+ (unsigned long)(sport->dma_rx_desc);
+ local_irq_restore(flags);
+ sport->curr_rx_desc = sport->dma_rx_desc;
+ } else {
+ sport_tx_dma_start(sport, 1);
+ sport_rx_dma_start(sport, 0);
+ sport_start(sport);
+ }
+
+ sport->rx_run = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_rx_start);
+
+int sport_rx_stop(struct sport_device *sport)
+{
+ pr_debug("%s enter\n", __func__);
+
+ if (!sport->rx_run)
+ return 0;
+ if (sport->tx_run) {
+ /* TX dma is still running, hook the dummy buffer */
+ sport_hook_rx_dummy(sport);
+ } else {
+ /* Both rx and tx dma will be stopped */
+ sport_stop(sport);
+ sport->curr_rx_desc = NULL;
+ sport->curr_tx_desc = NULL;
+ }
+
+ sport->rx_run = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_rx_stop);
+
+static inline int sport_hook_tx_dummy(struct sport_device *sport)
+{
+ struct dmasg *desc, temp_desc;
+ unsigned long flags;
+
+ BUG_ON(sport->dummy_tx_desc == NULL);
+ BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc);
+
+ sport->dummy_tx_desc->next_desc_addr = \
+ (unsigned long)(sport->dummy_tx_desc+1);
+
+ /* Shorten the time on last normal descriptor */
+ local_irq_save(flags);
+ desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan);
+ /* Store the descriptor which will be damaged */
+ temp_desc = *desc;
+ desc->x_count = 0xa;
+ desc->y_count = 0;
+ desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc);
+ local_irq_restore(flags);
+ /* Waiting for dummy buffer descriptor is already hooked*/
+ while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \
+ sizeof(struct dmasg)) != \
+ (unsigned long)sport->dummy_tx_desc)
+ ;
+ sport->curr_tx_desc = sport->dummy_tx_desc;
+ /* Restore the damaged descriptor */
+ *desc = temp_desc;
+
+ return 0;
+}
+
+int sport_tx_start(struct sport_device *sport)
+{
+ unsigned flags;
+ pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__,
+ sport->tx_run, sport->rx_run);
+ if (sport->tx_run)
+ return -EBUSY;
+ if (sport->rx_run) {
+ BUG_ON(sport->dma_tx_desc == NULL);
+ BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc);
+ /* Hook the normal buffer descriptor */
+ local_irq_save(flags);
+ while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) -
+ sizeof(struct dmasg)) !=
+ (unsigned long)sport->dummy_tx_desc)
+ ;
+ sport->dummy_tx_desc->next_desc_addr =
+ (unsigned long)(sport->dma_tx_desc);
+ local_irq_restore(flags);
+ sport->curr_tx_desc = sport->dma_tx_desc;
+ } else {
+
+ sport_tx_dma_start(sport, 0);
+ /* Let rx dma run the dummy buffer */
+ sport_rx_dma_start(sport, 1);
+ sport_start(sport);
+ }
+ sport->tx_run = 1;
+ return 0;
+}
+EXPORT_SYMBOL(sport_tx_start);
+
+int sport_tx_stop(struct sport_device *sport)
+{
+ if (!sport->tx_run)
+ return 0;
+ if (sport->rx_run) {
+ /* RX is still running, hook the dummy buffer */
+ sport_hook_tx_dummy(sport);
+ } else {
+ /* Both rx and tx dma stopped */
+ sport_stop(sport);
+ sport->curr_rx_desc = NULL;
+ sport->curr_tx_desc = NULL;
+ }
+
+ sport->tx_run = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_tx_stop);
+
+static inline int compute_wdsize(size_t wdsize)
+{
+ switch (wdsize) {
+ case 1:
+ return WDSIZE_8;
+ case 2:
+ return WDSIZE_16;
+ case 4:
+ default:
+ return WDSIZE_32;
+ }
+}
+
+int sport_config_rx_dma(struct sport_device *sport, void *buf,
+ int fragcount, size_t fragsize)
+{
+ unsigned int x_count;
+ unsigned int y_count;
+ unsigned int cfg;
+ dma_addr_t addr;
+
+ pr_debug("%s buf:%p, frag:%d, fragsize:0x%lx\n", __func__, \
+ buf, fragcount, fragsize);
+
+ x_count = fragsize / sport->wdsize;
+ y_count = 0;
+
+ /* for fragments larger than 64k words we use 2d dma,
+ * denote fragecount as two numbers' mutliply and both of them
+ * are less than 64k.*/
+ if (x_count >= 0x10000) {
+ int i, count = x_count;
+
+ for (i = 16; i > 0; i--) {
+ x_count = 1 << i;
+ if ((count & (x_count - 1)) == 0) {
+ y_count = count >> i;
+ if (y_count < 0x10000)
+ break;
+ }
+ }
+ if (i == 0)
+ return -EINVAL;
+ }
+ pr_debug("%s(x_count:0x%x, y_count:0x%x)\n", __func__,
+ x_count, y_count);
+
+ if (sport->dma_rx_desc)
+ dma_free_coherent(NULL, sport->rx_desc_bytes,
+ sport->dma_rx_desc, 0);
+
+ /* Allocate a new descritor ring as current one. */
+ sport->dma_rx_desc = dma_alloc_coherent(NULL, \
+ fragcount * sizeof(struct dmasg), &addr, 0);
+ sport->rx_desc_bytes = fragcount * sizeof(struct dmasg);
+
+ if (!sport->dma_rx_desc) {
+ pr_err("Failed to allocate memory for rx desc\n");
+ return -ENOMEM;
+ }
+
+ sport->rx_buf = buf;
+ sport->rx_fragsize = fragsize;
+ sport->rx_frags = fragcount;
+
+ cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | WNR | \
+ (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
+
+ if (y_count != 0)
+ cfg |= DMA2D;
+
+ setup_desc(sport->dma_rx_desc, buf, fragcount, fragsize,
+ cfg|DMAEN, x_count, y_count, sport->wdsize);
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_config_rx_dma);
+
+int sport_config_tx_dma(struct sport_device *sport, void *buf, \
+ int fragcount, size_t fragsize)
+{
+ unsigned int x_count;
+ unsigned int y_count;
+ unsigned int cfg;
+ dma_addr_t addr;
+
+ pr_debug("%s buf:%p, fragcount:%d, fragsize:0x%lx\n",
+ __func__, buf, fragcount, fragsize);
+
+ x_count = fragsize/sport->wdsize;
+ y_count = 0;
+
+ /* for fragments larger than 64k words we use 2d dma,
+ * denote fragecount as two numbers' mutliply and both of them
+ * are less than 64k.*/
+ if (x_count >= 0x10000) {
+ int i, count = x_count;
+
+ for (i = 16; i > 0; i--) {
+ x_count = 1 << i;
+ if ((count & (x_count - 1)) == 0) {
+ y_count = count >> i;
+ if (y_count < 0x10000)
+ break;
+ }
+ }
+ if (i == 0)
+ return -EINVAL;
+ }
+ pr_debug("%s x_count:0x%x, y_count:0x%x\n", __func__,
+ x_count, y_count);
+
+
+ if (sport->dma_tx_desc) {
+ dma_free_coherent(NULL, sport->tx_desc_bytes, \
+ sport->dma_tx_desc, 0);
+ }
+
+ sport->dma_tx_desc = dma_alloc_coherent(NULL, \
+ fragcount * sizeof(struct dmasg), &addr, 0);
+ sport->tx_desc_bytes = fragcount * sizeof(struct dmasg);
+ if (!sport->dma_tx_desc) {
+ pr_err("Failed to allocate memory for tx desc\n");
+ return -ENOMEM;
+ }
+
+ sport->tx_buf = buf;
+ sport->tx_fragsize = fragsize;
+ sport->tx_frags = fragcount;
+ cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | \
+ (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
+
+ if (y_count != 0)
+ cfg |= DMA2D;
+
+ setup_desc(sport->dma_tx_desc, buf, fragcount, fragsize,
+ cfg|DMAEN, x_count, y_count, sport->wdsize);
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_config_tx_dma);
+
+/* setup dummy dma descriptor ring, which don't generate interrupts,
+ * the x_modify is set to 0 */
+static int sport_config_rx_dummy(struct sport_device *sport)
+{
+ struct dmasg *desc;
+ unsigned config;
+
+ pr_debug("%s entered\n", __func__);
+#if L1_DATA_A_LENGTH != 0
+ desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
+#else
+ {
+ dma_addr_t addr;
+ desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
+ }
+#endif
+ if (desc == NULL) {
+ pr_err("Failed to allocate memory for dummy rx desc\n");
+ return -ENOMEM;
+ }
+ memset(desc, 0, 2 * sizeof(*desc));
+ sport->dummy_rx_desc = desc;
+ desc->start_addr = (unsigned long)sport->dummy_buf;
+ config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize)
+ | WNR | DMAEN;
+ desc->cfg = config;
+ desc->x_count = sport->dummy_count/sport->wdsize;
+ desc->x_modify = sport->wdsize;
+ desc->y_count = 0;
+ desc->y_modify = 0;
+ memcpy(desc+1, desc, sizeof(*desc));
+ desc->next_desc_addr = (unsigned long)(desc+1);
+ desc[1].next_desc_addr = (unsigned long)desc;
+ return 0;
+}
+
+static int sport_config_tx_dummy(struct sport_device *sport)
+{
+ struct dmasg *desc;
+ unsigned int config;
+
+ pr_debug("%s entered\n", __func__);
+
+#if L1_DATA_A_LENGTH != 0
+ desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
+#else
+ {
+ dma_addr_t addr;
+ desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
+ }
+#endif
+ if (!desc) {
+ pr_err("Failed to allocate memory for dummy tx desc\n");
+ return -ENOMEM;
+ }
+ memset(desc, 0, 2 * sizeof(*desc));
+ sport->dummy_tx_desc = desc;
+ desc->start_addr = (unsigned long)sport->dummy_buf + \
+ sport->dummy_count;
+ config = DMAFLOW_LARGE | NDSIZE_9 |
+ compute_wdsize(sport->wdsize) | DMAEN;
+ desc->cfg = config;
+ desc->x_count = sport->dummy_count/sport->wdsize;
+ desc->x_modify = sport->wdsize;
+ desc->y_count = 0;
+ desc->y_modify = 0;
+ memcpy(desc+1, desc, sizeof(*desc));
+ desc->next_desc_addr = (unsigned long)(desc+1);
+ desc[1].next_desc_addr = (unsigned long)desc;
+ return 0;
+}
+
+unsigned long sport_curr_offset_rx(struct sport_device *sport)
+{
+ unsigned long curr = get_dma_curr_addr(sport->dma_rx_chan);
+
+ return (unsigned char *)curr - sport->rx_buf;
+}
+EXPORT_SYMBOL(sport_curr_offset_rx);
+
+unsigned long sport_curr_offset_tx(struct sport_device *sport)
+{
+ unsigned long curr = get_dma_curr_addr(sport->dma_tx_chan);
+
+ return (unsigned char *)curr - sport->tx_buf;
+}
+EXPORT_SYMBOL(sport_curr_offset_tx);
+
+void sport_incfrag(struct sport_device *sport, int *frag, int tx)
+{
+ ++(*frag);
+ if (tx == 1 && *frag == sport->tx_frags)
+ *frag = 0;
+
+ if (tx == 0 && *frag == sport->rx_frags)
+ *frag = 0;
+}
+EXPORT_SYMBOL(sport_incfrag);
+
+void sport_decfrag(struct sport_device *sport, int *frag, int tx)
+{
+ --(*frag);
+ if (tx == 1 && *frag == 0)
+ *frag = sport->tx_frags;
+
+ if (tx == 0 && *frag == 0)
+ *frag = sport->rx_frags;
+}
+EXPORT_SYMBOL(sport_decfrag);
+
+static int sport_check_status(struct sport_device *sport,
+ unsigned int *sport_stat,
+ unsigned int *rx_stat,
+ unsigned int *tx_stat)
+{
+ int status = 0;
+
+ if (sport_stat) {
+ SSYNC();
+ status = sport->regs->stat;
+ if (status & (TOVF|TUVF|ROVF|RUVF))
+ sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
+ SSYNC();
+ *sport_stat = status;
+ }
+
+ if (rx_stat) {
+ SSYNC();
+ status = get_dma_curr_irqstat(sport->dma_rx_chan);
+ if (status & (DMA_DONE|DMA_ERR))
+ clear_dma_irqstat(sport->dma_rx_chan);
+ SSYNC();
+ *rx_stat = status;
+ }
+
+ if (tx_stat) {
+ SSYNC();
+ status = get_dma_curr_irqstat(sport->dma_tx_chan);
+ if (status & (DMA_DONE|DMA_ERR))
+ clear_dma_irqstat(sport->dma_tx_chan);
+ SSYNC();
+ *tx_stat = status;
+ }
+
+ return 0;
+}
+
+int sport_dump_stat(struct sport_device *sport, char *buf, size_t len)
+{
+ int ret;
+
+ ret = snprintf(buf, len,
+ "sts: 0x%04x\n"
+ "rx dma %d sts: 0x%04x tx dma %d sts: 0x%04x\n",
+ sport->regs->stat,
+ sport->dma_rx_chan,
+ get_dma_curr_irqstat(sport->dma_rx_chan),
+ sport->dma_tx_chan,
+ get_dma_curr_irqstat(sport->dma_tx_chan));
+ buf += ret;
+ len -= ret;
+
+ ret += snprintf(buf, len,
+ "curr_rx_desc:0x%p, curr_tx_desc:0x%p\n"
+ "dma_rx_desc:0x%p, dma_tx_desc:0x%p\n"
+ "dummy_rx_desc:0x%p, dummy_tx_desc:0x%p\n",
+ sport->curr_rx_desc, sport->curr_tx_desc,
+ sport->dma_rx_desc, sport->dma_tx_desc,
+ sport->dummy_rx_desc, sport->dummy_tx_desc);
+
+ return ret;
+}
+
+static irqreturn_t rx_handler(int irq, void *dev_id)
+{
+ unsigned int rx_stat;
+ struct sport_device *sport = dev_id;
+
+ pr_debug("%s enter\n", __func__);
+ sport_check_status(sport, NULL, &rx_stat, NULL);
+ if (!(rx_stat & DMA_DONE))
+ pr_err("rx dma is already stopped\n");
+
+ if (sport->rx_callback) {
+ sport->rx_callback(sport->rx_data);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t tx_handler(int irq, void *dev_id)
+{
+ unsigned int tx_stat;
+ struct sport_device *sport = dev_id;
+ pr_debug("%s enter\n", __func__);
+ sport_check_status(sport, NULL, NULL, &tx_stat);
+ if (!(tx_stat & DMA_DONE)) {
+ pr_err("tx dma is already stopped\n");
+ return IRQ_HANDLED;
+ }
+ if (sport->tx_callback) {
+ sport->tx_callback(sport->tx_data);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t err_handler(int irq, void *dev_id)
+{
+ unsigned int status = 0;
+ struct sport_device *sport = dev_id;
+
+ pr_debug("%s\n", __func__);
+ if (sport_check_status(sport, &status, NULL, NULL)) {
+ pr_err("error checking status ??");
+ return IRQ_NONE;
+ }
+
+ if (status & (TOVF|TUVF|ROVF|RUVF)) {
+ pr_info("sport status error:%s%s%s%s\n",
+ status & TOVF ? " TOVF" : "",
+ status & TUVF ? " TUVF" : "",
+ status & ROVF ? " ROVF" : "",
+ status & RUVF ? " RUVF" : "");
+ if (status & TOVF || status & TUVF) {
+ disable_dma(sport->dma_tx_chan);
+ if (sport->tx_run)
+ sport_tx_dma_start(sport, 0);
+ else
+ sport_tx_dma_start(sport, 1);
+ enable_dma(sport->dma_tx_chan);
+ } else {
+ disable_dma(sport->dma_rx_chan);
+ if (sport->rx_run)
+ sport_rx_dma_start(sport, 0);
+ else
+ sport_rx_dma_start(sport, 1);
+ enable_dma(sport->dma_rx_chan);
+ }
+ }
+ status = sport->regs->stat;
+ if (status & (TOVF|TUVF|ROVF|RUVF))
+ sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
+ SSYNC();
+
+ if (sport->err_callback)
+ sport->err_callback(sport->err_data);
+
+ return IRQ_HANDLED;
+}
+
+int sport_set_rx_callback(struct sport_device *sport,
+ void (*rx_callback)(void *), void *rx_data)
+{
+ BUG_ON(rx_callback == NULL);
+ sport->rx_callback = rx_callback;
+ sport->rx_data = rx_data;
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_set_rx_callback);
+
+int sport_set_tx_callback(struct sport_device *sport,
+ void (*tx_callback)(void *), void *tx_data)
+{
+ BUG_ON(tx_callback == NULL);
+ sport->tx_callback = tx_callback;
+ sport->tx_data = tx_data;
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_set_tx_callback);
+
+int sport_set_err_callback(struct sport_device *sport,
+ void (*err_callback)(void *), void *err_data)
+{
+ BUG_ON(err_callback == NULL);
+ sport->err_callback = err_callback;
+ sport->err_data = err_data;
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_set_err_callback);
+
+struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
+ unsigned dummy_count, void *private_data)
+{
+ int ret;
+ struct sport_device *sport;
+ pr_debug("%s enter\n", __func__);
+ BUG_ON(param == NULL);
+ BUG_ON(wdsize == 0 || dummy_count == 0);
+ sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
+ if (!sport) {
+ pr_err("Failed to allocate for sport device\n");
+ return NULL;
+ }
+
+ memset(sport, 0, sizeof(struct sport_device));
+ sport->dma_rx_chan = param->dma_rx_chan;
+ sport->dma_tx_chan = param->dma_tx_chan;
+ sport->err_irq = param->err_irq;
+ sport->regs = param->regs;
+ sport->private_data = private_data;
+
+ if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
+ pr_err("Failed to request RX dma %d\n", \
+ sport->dma_rx_chan);
+ goto __init_err1;
+ }
+ if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
+ pr_err("Failed to request RX irq %d\n", \
+ sport->dma_rx_chan);
+ goto __init_err2;
+ }
+
+ if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
+ pr_err("Failed to request TX dma %d\n", \
+ sport->dma_tx_chan);
+ goto __init_err2;
+ }
+
+ if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
+ pr_err("Failed to request TX irq %d\n", \
+ sport->dma_tx_chan);
+ goto __init_err3;
+ }
+
+ if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
+ sport) < 0) {
+ pr_err("Failed to request err irq:%d\n", \
+ sport->err_irq);
+ goto __init_err3;
+ }
+
+ pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
+ sport->dma_rx_chan, sport->dma_tx_chan,
+ sport->err_irq, sport->regs);
+
+ sport->wdsize = wdsize;
+ sport->dummy_count = dummy_count;
+
+#if L1_DATA_A_LENGTH != 0
+ sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2);
+#else
+ sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL);
+#endif
+ if (sport->dummy_buf == NULL) {
+ pr_err("Failed to allocate dummy buffer\n");
+ goto __error;
+ }
+
+ memset(sport->dummy_buf, 0, dummy_count * 2);
+ ret = sport_config_rx_dummy(sport);
+ if (ret) {
+ pr_err("Failed to config rx dummy ring\n");
+ goto __error;
+ }
+ ret = sport_config_tx_dummy(sport);
+ if (ret) {
+ pr_err("Failed to config tx dummy ring\n");
+ goto __error;
+ }
+
+ return sport;
+__error:
+ free_irq(sport->err_irq, sport);
+__init_err3:
+ free_dma(sport->dma_tx_chan);
+__init_err2:
+ free_dma(sport->dma_rx_chan);
+__init_err1:
+ kfree(sport);
+ return NULL;
+}
+EXPORT_SYMBOL(sport_init);
+
+void sport_done(struct sport_device *sport)
+{
+ if (sport == NULL)
+ return;
+
+ sport_stop(sport);
+ if (sport->dma_rx_desc)
+ dma_free_coherent(NULL, sport->rx_desc_bytes,
+ sport->dma_rx_desc, 0);
+ if (sport->dma_tx_desc)
+ dma_free_coherent(NULL, sport->tx_desc_bytes,
+ sport->dma_tx_desc, 0);
+
+#if L1_DATA_A_LENGTH != 0
+ l1_data_sram_free(sport->dummy_rx_desc);
+ l1_data_sram_free(sport->dummy_tx_desc);
+ l1_data_sram_free(sport->dummy_buf);
+#else
+ dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+ sport->dummy_rx_desc, 0);
+ dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+ sport->dummy_tx_desc, 0);
+ kfree(sport->dummy_buf);
+#endif
+ free_dma(sport->dma_rx_chan);
+ free_dma(sport->dma_tx_chan);
+ free_irq(sport->err_irq, sport);
+
+ kfree(sport);
+ sport = NULL;
+}
+EXPORT_SYMBOL(sport_done);
+/*
+* It is only used to send several bytes when dma is not enabled
+ * sport controller is configured but not enabled.
+ * Multichannel cannot works with pio mode */
+/* Used by ac97 to write and read codec register */
+int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
+ u8 *in_data, int len)
+{
+ unsigned short dma_config;
+ unsigned short status;
+ unsigned long flags;
+ unsigned long wait = 0;
+
+ pr_debug("%s enter, out_data:%p, in_data:%p len:%d\n", \
+ __func__, out_data, in_data, len);
+ pr_debug("tcr1:0x%04x, tcr2:0x%04x, tclkdiv:0x%04x, tfsdiv:0x%04x\n"
+ "mcmc1:0x%04x, mcmc2:0x%04x\n",
+ sport->regs->tcr1, sport->regs->tcr2,
+ sport->regs->tclkdiv, sport->regs->tfsdiv,
+ sport->regs->mcmc1, sport->regs->mcmc2);
+ flush_dcache_range((unsigned)out_data, (unsigned)(out_data + len));
+
+ /* Enable tx dma */
+ dma_config = (RESTART | WDSIZE_16 | DI_EN);
+ set_dma_start_addr(sport->dma_tx_chan, (unsigned long)out_data);
+ set_dma_x_count(sport->dma_tx_chan, len/2);
+ set_dma_x_modify(sport->dma_tx_chan, 2);
+ set_dma_config(sport->dma_tx_chan, dma_config);
+ enable_dma(sport->dma_tx_chan);
+
+ if (in_data != NULL) {
+ invalidate_dcache_range((unsigned)in_data, \
+ (unsigned)(in_data + len));
+ /* Enable rx dma */
+ dma_config = (RESTART | WDSIZE_16 | WNR | DI_EN);
+ set_dma_start_addr(sport->dma_rx_chan, (unsigned long)in_data);
+ set_dma_x_count(sport->dma_rx_chan, len/2);
+ set_dma_x_modify(sport->dma_rx_chan, 2);
+ set_dma_config(sport->dma_rx_chan, dma_config);
+ enable_dma(sport->dma_rx_chan);
+ }
+
+ local_irq_save(flags);
+ sport->regs->tcr1 |= TSPEN;
+ sport->regs->rcr1 |= RSPEN;
+ SSYNC();
+
+ status = get_dma_curr_irqstat(sport->dma_tx_chan);
+ while (status & DMA_RUN) {
+ udelay(1);
+ status = get_dma_curr_irqstat(sport->dma_tx_chan);
+ pr_debug("DMA status:0x%04x\n", status);
+ if (wait++ > 100)
+ goto __over;
+ }
+ status = sport->regs->stat;
+ wait = 0;
+
+ while (!(status & TXHRE)) {
+ pr_debug("sport status:0x%04x\n", status);
+ udelay(1);
+ status = *(unsigned short *)&sport->regs->stat;
+ if (wait++ > 1000)
+ goto __over;
+ }
+ /* Wait for the last byte sent out */
+ udelay(20);
+ pr_debug("sport status:0x%04x\n", status);
+
+__over:
+ sport->regs->tcr1 &= ~TSPEN;
+ sport->regs->rcr1 &= ~RSPEN;
+ SSYNC();
+ disable_dma(sport->dma_tx_chan);
+ /* Clear the status */
+ clear_dma_irqstat(sport->dma_tx_chan);
+ if (in_data != NULL) {
+ disable_dma(sport->dma_rx_chan);
+ clear_dma_irqstat(sport->dma_rx_chan);
+ }
+ SSYNC();
+ local_irq_restore(flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(sport_send_and_recv);
+
+MODULE_AUTHOR("Roy Huang");
+MODULE_DESCRIPTION("SPORT driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
new file mode 100644
index 00000000000..fcadcc081f7
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -0,0 +1,194 @@
+/*
+ * File: bf5xx_ac97_sport.h
+ * Based on:
+ * Author: Roy Huang <roy.huang@analog.com>
+ *
+ * Created:
+ * Description:
+ *
+ * Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef __BF5XX_SPORT_H__
+#define __BF5XX_SPORT_H__
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <asm/dma.h>
+
+struct sport_register {
+ u16 tcr1; u16 reserved0;
+ u16 tcr2; u16 reserved1;
+ u16 tclkdiv; u16 reserved2;
+ u16 tfsdiv; u16 reserved3;
+ u32 tx;
+ u32 reserved_l0;
+ u32 rx;
+ u32 reserved_l1;
+ u16 rcr1; u16 reserved4;
+ u16 rcr2; u16 reserved5;
+ u16 rclkdiv; u16 reserved6;
+ u16 rfsdiv; u16 reserved7;
+ u16 stat; u16 reserved8;
+ u16 chnl; u16 reserved9;
+ u16 mcmc1; u16 reserved10;
+ u16 mcmc2; u16 reserved11;
+ u32 mtcs0;
+ u32 mtcs1;
+ u32 mtcs2;
+ u32 mtcs3;
+ u32 mrcs0;
+ u32 mrcs1;
+ u32 mrcs2;
+ u32 mrcs3;
+};
+
+#define DESC_ELEMENT_COUNT 9
+
+struct sport_device {
+ int dma_rx_chan;
+ int dma_tx_chan;
+ int err_irq;
+ struct sport_register *regs;
+
+ unsigned char *rx_buf;
+ unsigned char *tx_buf;
+ unsigned int rx_fragsize;
+ unsigned int tx_fragsize;
+ unsigned int rx_frags;
+ unsigned int tx_frags;
+ unsigned int wdsize;
+
+ /* for dummy dma transfer */
+ void *dummy_buf;
+ unsigned int dummy_count;
+
+ /* DMA descriptor ring head of current audio stream*/
+ struct dmasg *dma_rx_desc;
+ struct dmasg *dma_tx_desc;
+ unsigned int rx_desc_bytes;
+ unsigned int tx_desc_bytes;
+
+ unsigned int rx_run:1; /* rx is running */
+ unsigned int tx_run:1; /* tx is running */
+
+ struct dmasg *dummy_rx_desc;
+ struct dmasg *dummy_tx_desc;
+
+ struct dmasg *curr_rx_desc;
+ struct dmasg *curr_tx_desc;
+
+ int rx_curr_frag;
+ int tx_curr_frag;
+
+ unsigned int rcr1;
+ unsigned int rcr2;
+ int rx_tdm_count;
+
+ unsigned int tcr1;
+ unsigned int tcr2;
+ int tx_tdm_count;
+
+ void (*rx_callback)(void *data);
+ void *rx_data;
+ void (*tx_callback)(void *data);
+ void *tx_data;
+ void (*err_callback)(void *data);
+ void *err_data;
+ unsigned char *tx_dma_buf;
+ unsigned char *rx_dma_buf;
+#ifdef CONFIG_SND_MMAP_SUPPORT
+ dma_addr_t tx_dma_phy;
+ dma_addr_t rx_dma_phy;
+ int tx_pos;/*pcm sample count*/
+ int rx_pos;
+ unsigned int tx_buffer_size;
+ unsigned int rx_buffer_size;
+ int tx_delay_pos;
+ int once;
+#endif
+ void *private_data;
+};
+
+extern struct sport_device *sport_handle;
+
+struct sport_param {
+ int dma_rx_chan;
+ int dma_tx_chan;
+ int err_irq;
+ struct sport_register *regs;
+};
+
+struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
+ unsigned dummy_count, void *private_data);
+
+void sport_done(struct sport_device *sport);
+
+/* first use these ...*/
+
+/* note: multichannel is in units of 8 channels, tdm_count is number of channels
+ * NOT / 8 ! all channels are enabled by default */
+int sport_set_multichannel(struct sport_device *sport, int tdm_count,
+ u32 mask, int packed);
+
+int sport_config_rx(struct sport_device *sport,
+ unsigned int rcr1, unsigned int rcr2,
+ unsigned int clkdiv, unsigned int fsdiv);
+
+int sport_config_tx(struct sport_device *sport,
+ unsigned int tcr1, unsigned int tcr2,
+ unsigned int clkdiv, unsigned int fsdiv);
+
+/* ... then these: */
+
+/* buffer size (in bytes) == fragcount * fragsize_bytes */
+
+/* this is not a very general api, it sets the dma to 2d autobuffer mode */
+
+int sport_config_rx_dma(struct sport_device *sport, void *buf,
+ int fragcount, size_t fragsize_bytes);
+
+int sport_config_tx_dma(struct sport_device *sport, void *buf,
+ int fragcount, size_t fragsize_bytes);
+
+int sport_tx_start(struct sport_device *sport);
+int sport_tx_stop(struct sport_device *sport);
+int sport_rx_start(struct sport_device *sport);
+int sport_rx_stop(struct sport_device *sport);
+
+/* for use in interrupt handler */
+unsigned long sport_curr_offset_rx(struct sport_device *sport);
+unsigned long sport_curr_offset_tx(struct sport_device *sport);
+
+void sport_incfrag(struct sport_device *sport, int *frag, int tx);
+void sport_decfrag(struct sport_device *sport, int *frag, int tx);
+
+int sport_set_rx_callback(struct sport_device *sport,
+ void (*rx_callback)(void *), void *rx_data);
+int sport_set_tx_callback(struct sport_device *sport,
+ void (*tx_callback)(void *), void *tx_data);
+int sport_set_err_callback(struct sport_device *sport,
+ void (*err_callback)(void *), void *err_data);
+
+int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
+ u8 *in_data, int len);
+#endif /* BF53X_SPORT_H */
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
new file mode 100644
index 00000000000..e15f67fd776
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -0,0 +1,186 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ssm2602.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ * Description: board driver for SSM2602 sound chip
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <linux/gpio.h>
+#include "../codecs/ssm2602.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+static struct snd_soc_machine bf5xx_ssm2602;
+
+static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ pr_debug("%s enter\n", __func__);
+ cpu_dai->private_data = sport_handle;
+ return 0;
+}
+
+static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
+ params_format(params));
+ /*
+ * If you are using a crystal source which frequency is not 12MHz
+ * then modify the below case statement with frequency of the crystal.
+ *
+ * If you are using the SPORT to generate clocking then this is
+ * where to do it.
+ */
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 12000000;
+ break;
+ }
+
+ /*
+ * CODEC is master for BCLK and LRC in this configuration.
+ */
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops bf5xx_ssm2602_ops = {
+ .startup = bf5xx_ssm2602_startup,
+ .hw_params = bf5xx_ssm2602_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai = &bf5xx_i2s_dai,
+ .codec_dai = &ssm2602_dai,
+ .ops = &bf5xx_ssm2602_ops,
+};
+
+/*
+ * SSM2602 2 wire address is determined by CSB
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+
+static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
+ .i2c_bus = 0,
+ .i2c_address = 0x1b,
+};
+
+static struct snd_soc_machine bf5xx_ssm2602 = {
+ .name = "bf5xx_ssm2602",
+ .dai_link = &bf5xx_ssm2602_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
+ .machine = &bf5xx_ssm2602,
+ .platform = &bf5xx_i2s_soc_platform,
+ .codec_dev = &soc_codec_dev_ssm2602,
+ .codec_data = &bf5xx_ssm2602_setup,
+};
+
+static struct platform_device *bf52x_ssm2602_snd_device;
+
+static int __init bf5xx_ssm2602_init(void)
+{
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+ bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bf52x_ssm2602_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(bf52x_ssm2602_snd_device,
+ &bf5xx_ssm2602_snd_devdata);
+ bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
+ ret = platform_device_add(bf52x_ssm2602_snd_device);
+
+ if (ret)
+ platform_device_put(bf52x_ssm2602_snd_device);
+
+ return ret;
+}
+
+static void __exit bf5xx_ssm2602_exit(void)
+{
+ pr_debug("%s enter\n", __func__);
+ platform_device_unregister(bf52x_ssm2602_snd_device);
+}
+
+module_init(bf5xx_ssm2602_init);
+module_exit(bf5xx_ssm2602_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC SSM2602 BF527-EZKIT");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3903ab7dfa4..38a0e3b620a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,31 +1,50 @@
-config SND_SOC_AC97_CODEC
- tristate
- depends on SND_SOC
+config SND_SOC_ALL_CODECS
+ tristate "Build all ASoC CODEC drivers"
+ depends on I2C
+ select SPI
+ select SPI_MASTER
+ select SND_SOC_AD73311
+ select SND_SOC_AK4535
+ select SND_SOC_CS4270
+ select SND_SOC_SSM2602
+ select SND_SOC_TLV320AIC23
+ select SND_SOC_TLV320AIC26
+ select SND_SOC_TLV320AIC3X
+ select SND_SOC_UDA1380
+ select SND_SOC_WM8510
+ select SND_SOC_WM8580
+ select SND_SOC_WM8731
+ select SND_SOC_WM8750
+ select SND_SOC_WM8753
+ select SND_SOC_WM8900
+ select SND_SOC_WM8903
+ select SND_SOC_WM8971
+ select SND_SOC_WM8990
+ help
+ Normally ASoC codec drivers are only built if a machine driver which
+ uses them is also built since they are only usable with a machine
+ driver. Selecting this option will allow these drivers to be built
+ without an explicit machine driver for test and development purposes.
-config SND_SOC_WM8731
- tristate
- depends on SND_SOC
+ If unsure select "N".
-config SND_SOC_WM8750
+
+config SND_SOC_AC97_CODEC
tristate
- depends on SND_SOC
+ select SND_AC97_CODEC
-config SND_SOC_WM8753
+config SND_SOC_AD1980
tristate
- depends on SND_SOC
-config SND_SOC_WM9712
+config SND_SOC_AD73311
tristate
- depends on SND_SOC
-config SND_SOC_WM9713
+config SND_SOC_AK4535
tristate
- depends on SND_SOC
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
- depends on SND_SOC
# Cirrus Logic CS4270 Codec Hardware Mute Support
# Select if you have external muting circuitry attached to your CS4270.
@@ -41,6 +60,53 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
+config SND_SOC_SSM2602
+ tristate
+
+config SND_SOC_TLV320AIC23
+ tristate
+ depends on I2C
+
+config SND_SOC_TLV320AIC26
+ tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
+ depends on SPI
+
config SND_SOC_TLV320AIC3X
tristate
- depends on SND_SOC && I2C
+ depends on I2C
+
+config SND_SOC_UDA1380
+ tristate
+
+config SND_SOC_WM8510
+ tristate
+
+config SND_SOC_WM8580
+ tristate
+
+config SND_SOC_WM8731
+ tristate
+
+config SND_SOC_WM8750
+ tristate
+
+config SND_SOC_WM8753
+ tristate
+
+config SND_SOC_WM8900
+ tristate
+
+config SND_SOC_WM8903
+ tristate
+
+config SND_SOC_WM8971
+ tristate
+
+config SND_SOC_WM8990
+ tristate
+
+config SND_SOC_WM9712
+ tristate
+
+config SND_SOC_WM9713
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4e1314c9d3e..90f0a585fc7 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,17 +1,43 @@
snd-soc-ac97-objs := ac97.o
+snd-soc-ad1980-objs := ad1980.o
+snd-soc-ad73311-objs := ad73311.o
+snd-soc-ak4535-objs := ak4535.o
+snd-soc-cs4270-objs := cs4270.o
+snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-tlv320aic23-objs := tlv320aic23.o
+snd-soc-tlv320aic26-objs := tlv320aic26.o
+snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8510-objs := wm8510.o
+snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8900-objs := wm8900.o
+snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm8990-objs := wm8990.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.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_AD1980) += snd-soc-ad1980.o
+obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
+obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
+obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
+obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
+obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.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 2a1ffe39690..bd1ebdc6c86 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -2,17 +2,13 @@
* ac97.c -- ALSA Soc AC97 codec support
*
* Copyright 2005 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 17th Oct 2005 Initial version.
- *
* Generic AC97 support.
*/
@@ -24,6 +20,7 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include "ac97.h"
#define AC97_VERSION "0.6"
@@ -43,7 +40,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
-struct snd_soc_codec_dai ac97_dai = {
+struct snd_soc_dai ac97_dai = {
.name = "AC97 HiFi",
.type = SND_SOC_DAI_AC97,
.playback = {
@@ -146,9 +143,34 @@ static int ac97_soc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int ac97_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_ac97_suspend(socdev->codec->ac97);
+
+ return 0;
+}
+
+static int ac97_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_ac97_resume(socdev->codec->ac97);
+
+ return 0;
+}
+#else
+#define ac97_soc_suspend NULL
+#define ac97_soc_resume NULL
+#endif
+
struct snd_soc_codec_device soc_codec_dev_ac97 = {
.probe = ac97_soc_probe,
.remove = ac97_soc_remove,
+ .suspend = ac97_soc_suspend,
+ .resume = ac97_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
index 2bf6d69fd06..281aa42e2bb 100644
--- a/sound/soc/codecs/ac97.h
+++ b/sound/soc/codecs/ac97.h
@@ -14,6 +14,6 @@
#define __LINUX_SND_SOC_AC97_H
extern struct snd_soc_codec_device soc_codec_dev_ac97;
-extern struct snd_soc_codec_dai ac97_dai;
+extern struct snd_soc_dai ac97_dai;
#endif
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
new file mode 100644
index 00000000000..1397b8e06c0
--- /dev/null
+++ b/sound/soc/codecs/ad1980.c
@@ -0,0 +1,308 @@
+/*
+ * ad1980.c -- ALSA Soc AD1980 codec support
+ *
+ * Copyright: Analog Device Inc.
+ * Author: Roy Huang <roy.huang@analog.com>
+ * Cliff Cai <cliff.cai@analog.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "ad1980.h"
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static int ac97_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int val);
+
+/*
+ * AD1980 register cache
+ */
+static const u16 ad1980_reg[] = {
+ 0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */
+ 0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */
+ 0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
+ 0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
+ 0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
+ 0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
+ 0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+ 0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+ 0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
+ 0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */
+};
+
+static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
+ "Stereo Mix", "Mono Mix", "Phone"};
+
+static const struct soc_enum ad1980_cap_src =
+ SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 7, ad1980_rec_sel);
+
+static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = {
+SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
+
+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+
+SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
+SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
+
+SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0),
+SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1),
+SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1),
+
+SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1),
+SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
+
+SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0),
+SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
+
+SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
+
+SOC_ENUM("Capture Source", ad1980_cap_src),
+
+SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
+};
+
+/* add non dapm controls */
+static int ad1980_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) {
+ err = snd_ctl_add(codec->card, snd_soc_cnew(
+ &ad1980_snd_ac97_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ switch (reg) {
+ case AC97_RESET:
+ case AC97_INT_PAGING:
+ case AC97_POWERDOWN:
+ case AC97_EXTENDED_STATUS:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return soc_ac97_ops.read(codec->ac97, reg);
+ default:
+ reg = reg >> 1;
+
+ if (reg >= (ARRAY_SIZE(ad1980_reg)))
+ return -EINVAL;
+
+ return cache[reg];
+ }
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ u16 *cache = codec->reg_cache;
+
+ soc_ac97_ops.write(codec->ac97, reg, val);
+ reg = reg >> 1;
+ if (reg < (ARRAY_SIZE(ad1980_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+struct snd_soc_dai ad1980_dai = {
+ .name = "AC97",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+EXPORT_SYMBOL_GPL(ad1980_dai);
+
+static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+{
+ u16 retry_cnt = 0;
+
+retry:
+ if (try_warm && soc_ac97_ops.warm_reset) {
+ soc_ac97_ops.warm_reset(codec->ac97);
+ if (ac97_read(codec, AC97_RESET) == 0x0090)
+ return 1;
+ }
+
+ soc_ac97_ops.reset(codec->ac97);
+ /* Set bit 16slot in register 74h, then every slot will has only 16
+ * bits. This command is sent out in 20bit mode, in which case the
+ * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
+ ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+
+ if (ac97_read(codec, AC97_RESET) != 0x0090)
+ goto err;
+ return 0;
+
+err:
+ while (retry_cnt++ < 10)
+ goto retry;
+
+ printk(KERN_ERR "AD1980 AC97 reset failed\n");
+ return -EIO;
+}
+
+static int ad1980_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+ u16 vendor_id2;
+
+ printk(KERN_INFO "AD1980 SoC Audio Codec\n");
+
+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->codec == NULL)
+ return -ENOMEM;
+ codec = socdev->codec;
+ mutex_init(&codec->mutex);
+
+ codec->reg_cache =
+ kzalloc(sizeof(u16) * ARRAY_SIZE(ad1980_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto cache_err;
+ }
+ memcpy(codec->reg_cache, ad1980_reg, sizeof(u16) * \
+ ARRAY_SIZE(ad1980_reg));
+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ad1980_reg);
+ codec->reg_cache_step = 2;
+ codec->name = "AD1980";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ad1980_dai;
+ codec->num_dai = 1;
+ codec->write = ac97_write;
+ codec->read = ac97_read;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+ goto codec_err;
+ }
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0)
+ goto pcm_err;
+
+
+ ret = ad1980_reset(codec, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "AC97 link error\n");
+ goto reset_err;
+ }
+
+ /* Read out vendor ID to make sure it is ad1980 */
+ if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
+ goto reset_err;
+
+ vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+
+ if (vendor_id2 != 0x5370) {
+ if (vendor_id2 != 0x5374)
+ goto reset_err;
+ else
+ printk(KERN_WARNING "ad1980: "
+ "Found AD1981 - only 2/2 IN/OUT Channels "
+ "supported\n");
+ }
+
+ ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */
+ ac97_write(codec, AC97_PCM, 0x0000); /* unmute PCM out volume */
+ ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */
+
+ ad1980_add_controls(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ad1980: failed to register card\n");
+ goto reset_err;
+ }
+
+ return 0;
+
+reset_err:
+ snd_soc_free_pcms(socdev);
+
+pcm_err:
+ snd_soc_free_ac97_codec(codec);
+
+codec_err:
+ kfree(codec->reg_cache);
+
+cache_err:
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+ return ret;
+}
+
+static int ad1980_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec == NULL)
+ return 0;
+
+ snd_soc_dapm_free(socdev);
+ snd_soc_free_pcms(socdev);
+ snd_soc_free_ac97_codec(codec);
+ kfree(codec->reg_cache);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad1980 = {
+ .probe = ad1980_soc_probe,
+ .remove = ad1980_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1980);
+
+MODULE_DESCRIPTION("ASoC ad1980 driver");
+MODULE_AUTHOR("Roy Huang, Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
new file mode 100644
index 00000000000..db6c8500d66
--- /dev/null
+++ b/sound/soc/codecs/ad1980.h
@@ -0,0 +1,23 @@
+/*
+ * ad1980.h -- ad1980 Soc Audio driver
+ */
+
+#ifndef _AD1980_H
+#define _AD1980_H
+/* Bit definition of Power-Down Control/Status Register */
+#define ADC 0x0001
+#define DAC 0x0002
+#define ANL 0x0004
+#define REF 0x0008
+#define PR0 0x0100
+#define PR1 0x0200
+#define PR2 0x0400
+#define PR3 0x0800
+#define PR4 0x1000
+#define PR5 0x2000
+#define PR6 0x4000
+
+extern struct snd_soc_dai ad1980_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1980;
+
+#endif
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
new file mode 100644
index 00000000000..37af8607b00
--- /dev/null
+++ b/sound/soc/codecs/ad73311.c
@@ -0,0 +1,107 @@
+/*
+ * ad73311.c -- ALSA Soc AD73311 codec support
+ *
+ * Copyright: Analog Device Inc.
+ * Author: Cliff Cai <cliff.cai@analog.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Revision history
+ * 25th Sep 2008 Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "ad73311.h"
+
+struct snd_soc_dai ad73311_dai = {
+ .name = "AD73311",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+EXPORT_SYMBOL_GPL(ad73311_dai);
+
+static int ad73311_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+ mutex_init(&codec->mutex);
+ codec->name = "AD73311";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ad73311_dai;
+ codec->num_dai = 1;
+ socdev->codec = codec;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ad73311: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ad73311: failed to register card\n");
+ goto register_err;
+ }
+
+ return ret;
+
+register_err:
+ snd_soc_free_pcms(socdev);
+pcm_err:
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+ return ret;
+}
+
+static int ad73311_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec == NULL)
+ return 0;
+ snd_soc_free_pcms(socdev);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad73311 = {
+ .probe = ad73311_soc_probe,
+ .remove = ad73311_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
+
+MODULE_DESCRIPTION("ASoC ad73311 driver");
+MODULE_AUTHOR("Cliff Cai ");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h
new file mode 100644
index 00000000000..507ce0c30ed
--- /dev/null
+++ b/sound/soc/codecs/ad73311.h
@@ -0,0 +1,90 @@
+/*
+ * File: sound/soc/codec/ad73311.h
+ * Based on:
+ * Author: Cliff Cai <cliff.cai@analog.com>
+ *
+ * Created: Thur Sep 25, 2008
+ * Description: definitions for AD73311 registers
+ *
+ *
+ * Modified:
+ * Copyright 2006 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __AD73311_H__
+#define __AD73311_H__
+
+#define AD_CONTROL 0x8000
+#define AD_DATA 0x0000
+#define AD_READ 0x4000
+#define AD_WRITE 0x0000
+
+/* Control register A */
+#define CTRL_REG_A (0 << 8)
+
+#define REGA_MODE_PRO 0x00
+#define REGA_MODE_DATA 0x01
+#define REGA_MODE_MIXED 0x03
+#define REGA_DLB 0x04
+#define REGA_SLB 0x08
+#define REGA_DEVC(x) ((x & 0x7) << 4)
+#define REGA_RESET 0x80
+
+/* Control register B */
+#define CTRL_REG_B (1 << 8)
+
+#define REGB_DIRATE(x) (x & 0x3)
+#define REGB_SCDIV(x) ((x & 0x3) << 2)
+#define REGB_MCDIV(x) ((x & 0x7) << 4)
+#define REGB_CEE (1 << 7)
+
+/* Control register C */
+#define CTRL_REG_C (2 << 8)
+
+#define REGC_PUDEV (1 << 0)
+#define REGC_PUADC (1 << 3)
+#define REGC_PUDAC (1 << 4)
+#define REGC_PUREF (1 << 5)
+#define REGC_REFUSE (1 << 6)
+
+/* Control register D */
+#define CTRL_REG_D (3 << 8)
+
+#define REGD_IGS(x) (x & 0x7)
+#define REGD_RMOD (1 << 3)
+#define REGD_OGS(x) ((x & 0x7) << 4)
+#define REGD_MUTE (x << 7)
+
+/* Control register E */
+#define CTRL_REG_E (4 << 8)
+
+#define REGE_DA(x) (x & 0x1f)
+#define REGE_IBYP (1 << 5)
+
+/* Control register F */
+#define CTRL_REG_F (5 << 8)
+
+#define REGF_SEEN (1 << 5)
+#define REGF_INV (1 << 6)
+#define REGF_ALB (1 << 7)
+
+extern struct snd_soc_dai ad73311_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad73311;
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
new file mode 100644
index 00000000000..2a89b5888e1
--- /dev/null
+++ b/sound/soc/codecs/ak4535.c
@@ -0,0 +1,694 @@
+/*
+ * ak4535.c -- AK4535 ALSA Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/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 "ak4535.h"
+
+#define AK4535_VERSION "0.3"
+
+struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+/* codec private data */
+struct ak4535_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * ak4535 register cache
+ */
+static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
+ 0x0000, 0x0080, 0x0000, 0x0003,
+ 0x0002, 0x0000, 0x0011, 0x0001,
+ 0x0000, 0x0040, 0x0036, 0x0010,
+ 0x0000, 0x0000, 0x0057, 0x0000,
+};
+
+/*
+ * read ak4535 register cache
+ */
+static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= AK4535_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 data;
+ data = reg;
+
+ if (codec->hw_write(codec->control_data, &data, 1) != 1)
+ return -EIO;
+
+ if (codec->hw_read(codec->control_data, &data, 1) != 1)
+ return -EIO;
+
+ return data;
+};
+
+/*
+ * write ak4535 register cache
+ */
+static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= AK4535_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the AK4535 register space
+ */
+static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D8 AK4535 register offset
+ * D7...D0 register data
+ */
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ ak4535_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static int ak4535_sync(struct snd_soc_codec *codec)
+{
+ u16 *cache = codec->reg_cache;
+ int i, r = 0;
+
+ for (i = 0; i < AK4535_CACHEREGNUM; i++)
+ r |= ak4535_write(codec, i, cache[i]);
+
+ return r;
+};
+
+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
+static const char *ak4535_mic_select[] = {"Internal", "External"};
+
+static const struct soc_enum ak4535_enum[] = {
+ SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
+ SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
+ SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
+ SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
+ SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
+};
+
+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
+ SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
+ SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
+ SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
+ SOC_ENUM("Headphone Output", ak4535_enum[2]),
+ SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
+ SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
+ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
+ SOC_ENUM("Mic Select", ak4535_enum[4]),
+ SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
+ SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
+ SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
+ SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
+ SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
+ SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
+ SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
+ SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
+ SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
+};
+
+/* add non dapm controls */
+static int ak4535_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4535_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4535_enum[4]);
+
+/* HP L switch */
+static const struct snd_kcontrol_new ak4535_hpl_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
+
+/* HP R switch */
+static const struct snd_kcontrol_new ak4535_hpr_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4535_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
+
+/* Line out switch */
+static const struct snd_kcontrol_new ak4535_line_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
+
+/* ak4535 dapm widgets */
+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4535_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_input_mixer_controls[0],
+ ARRAY_SIZE(ak4535_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4535_input_mux_control),
+ SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_mono2_control),
+ /* speaker powersave bit */
+ SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_line_control),
+ SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_hpl_control),
+ SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_hpr_control),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPP"),
+ SND_SOC_DAPM_OUTPUT("SPN"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+ SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("MIN"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /*stereo mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* mono1 mixer */
+ {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Input Mux", "Internal", "Mic Int Bias"},
+ {"Input Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Input Mux"},
+
+ /* line out */
+ {"LOUT", NULL, "Line Out Enable"},
+ {"ROUT", NULL, "Line Out Enable"},
+ {"Line Out Enable", "Switch", "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* mono1 out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* left HP */
+ {"HPL", NULL, "Left HP Enable"},
+ {"Left HP Enable", "Switch", "HP L Amp"},
+ {"HP L Amp", NULL, "Stereo Mixer"},
+
+ /* right HP */
+ {"HPR", NULL, "Right HP Enable"},
+ {"Right HP Enable", "Switch", "HP R Amp"},
+ {"HP R Amp", NULL, "Stereo Mixer"},
+
+ /* speaker */
+ {"SPP", NULL, "Speaker Enable"},
+ {"SPN", NULL, "Speaker Enable"},
+ {"Speaker Enable", "Switch", "Spk Amp"},
+ {"Spk Amp", NULL, "MIN"},
+
+ /* mono 2 */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Stereo Mixer"},
+
+ /* Aux In */
+ {"Aux In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "Aux In"},
+};
+
+static int ak4535_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets,
+ ARRAY_SIZE(ak4535_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4535_priv *ak4535 = codec->private_data;
+
+ ak4535->sysclk = freq;
+ return 0;
+}
+
+static int ak4535_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 ak4535_priv *ak4535 = codec->private_data;
+ u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
+ int rate = params_rate(params), fs = 256;
+
+ if (rate)
+ fs = ak4535->sysclk / rate;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 |= (0x2 << 5);
+ break;
+ case 512:
+ mode2 |= (0x1 << 5);
+ break;
+ case 256:
+ break;
+ }
+
+ /* set rate */
+ ak4535_write(codec, AK4535_MODE2, mode2);
+ return 0;
+}
+
+static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x0002;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x0001;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* use 32 fs for BCLK to save power */
+ mode1 |= 0x4;
+
+ ak4535_write(codec, AK4535_MODE1, mode1);
+ return 0;
+}
+
+static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
+ if (!mute)
+ ak4535_write(codec, AK4535_DAC, mute_reg);
+ else
+ ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
+ return 0;
+}
+
+static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 i;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ak4535_mute(codec->dai, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ ak4535_mute(codec->dai, 1);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ i = ak4535_read_reg_cache(codec, AK4535_PM1);
+ ak4535_write(codec, AK4535_PM1, i | 0x80);
+ i = ak4535_read_reg_cache(codec, AK4535_PM2);
+ ak4535_write(codec, AK4535_PM2, i & (~0x80));
+ break;
+ case SND_SOC_BIAS_OFF:
+ i = ak4535_read_reg_cache(codec, AK4535_PM1);
+ ak4535_write(codec, AK4535_PM1, i & (~0x80));
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai ak4535_dai = {
+ .name = "AK4535",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4535_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4535_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = ak4535_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = ak4535_set_dai_fmt,
+ .digital_mute = ak4535_mute,
+ .set_sysclk = ak4535_set_dai_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(ak4535_dai);
+
+static int ak4535_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;
+
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak4535_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ ak4535_sync(codec);
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ ak4535_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the AK4535 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ak4535_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "AK4535";
+ codec->owner = THIS_MODULE;
+ codec->read = ak4535_read_reg_cache;
+ codec->write = ak4535_write;
+ codec->set_bias_level = ak4535_set_bias_level;
+ codec->dai = &ak4535_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
+ codec->reg_cache = kmemdup(ak4535_reg, sizeof(ak4535_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ak4535: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ak4535_add_controls(codec);
+ ak4535_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ak4535: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+
+ return ret;
+}
+
+static struct snd_soc_device *ak4535_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static int ak4535_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = ak4535_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = ak4535_init(socdev);
+ if (ret < 0)
+ printk(KERN_ERR "failed to initialise AK4535\n");
+
+ return ret;
+}
+
+static int ak4535_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id ak4535_i2c_id[] = {
+ { "ak4535", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
+
+static struct i2c_driver ak4535_i2c_driver = {
+ .driver = {
+ .name = "AK4535 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4535_i2c_probe,
+ .remove = ak4535_i2c_remove,
+ .id_table = ak4535_i2c_id,
+};
+
+static int ak4535_add_i2c_device(struct platform_device *pdev,
+ const struct ak4535_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&ak4535_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "ak4535", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&ak4535_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+static int ak4535_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct ak4535_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct ak4535_priv *ak4535;
+ int ret;
+
+ printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
+ if (ak4535 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = ak4535;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ak4535_socdev = socdev;
+ ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->hw_read = (hw_read_t)i2c_master_recv;
+ ret = ak4535_add_i2c_device(pdev, setup);
+ }
+#endif
+
+ if (ret != 0) {
+ kfree(codec->private_data);
+ kfree(codec);
+ }
+ return ret;
+}
+
+/* power down chip */
+static int ak4535_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&ak4535_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4535 = {
+ .probe = ak4535_probe,
+ .remove = ak4535_remove,
+ .suspend = ak4535_suspend,
+ .resume = ak4535_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
+
+MODULE_DESCRIPTION("Soc AK4535 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
new file mode 100644
index 00000000000..c7a58703ea3
--- /dev/null
+++ b/sound/soc/codecs/ak4535.h
@@ -0,0 +1,47 @@
+/*
+ * ak4535.h -- AK4535 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AK4535_H
+#define _AK4535_H
+
+/* AK4535 register space */
+
+#define AK4535_PM1 0x0
+#define AK4535_PM2 0x1
+#define AK4535_SIG1 0x2
+#define AK4535_SIG2 0x3
+#define AK4535_MODE1 0x4
+#define AK4535_MODE2 0x5
+#define AK4535_DAC 0x6
+#define AK4535_MIC 0x7
+#define AK4535_TIMER 0x8
+#define AK4535_ALC1 0x9
+#define AK4535_ALC2 0xa
+#define AK4535_PGA 0xb
+#define AK4535_LATT 0xc
+#define AK4535_RATT 0xd
+#define AK4535_VOL 0xe
+#define AK4535_STATUS 0xf
+
+#define AK4535_CACHEREGNUM 0x10
+
+struct ak4535_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ak4535_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e73fcfd9f5c..0bbd94501d7 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -201,7 +201,7 @@ static struct {
* driver what the input settings can be. This would need to be implemented
* for stand-alone mode to work.
*/
-static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -251,7 +251,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
* data for playback only, but ASoC currently does not support different
* formats for playback vs. record.
*/
-static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -471,7 +471,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
* board does not have the MUTEA or MUTEB pins connected to such circuitry,
* then this function will do nothing.
*/
-static int cs4270_mute(struct snd_soc_codec_dai *dai, int mute)
+static int cs4270_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
int reg6;
@@ -490,34 +490,7 @@ static int cs4270_mute(struct snd_soc_codec_dai *dai, int mute)
#endif
-static int cs4270_i2c_probe(struct i2c_adapter *adap, int addr, int kind);
-
-/*
- * Notify the driver that a new I2C bus has been found.
- *
- * This function is called for each I2C bus in the system. The function
- * then asks the I2C subsystem to probe that bus at the addresses on which
- * our device (the CS4270) could exist. If a device is found at one of
- * those addresses, then our probe function (cs4270_i2c_probe) is called.
- */
-static int cs4270_i2c_attach(struct i2c_adapter *adapter)
-{
- return i2c_probe(adapter, &addr_data, cs4270_i2c_probe);
-}
-
-static int cs4270_i2c_detach(struct i2c_client *client)
-{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
-
- i2c_detach_client(client);
- codec->control_data = NULL;
-
- kfree(codec->reg_cache);
- codec->reg_cache = NULL;
-
- kfree(client);
- return 0;
-}
+static int cs4270_i2c_probe(struct i2c_client *, const struct i2c_device_id *);
/* A list of non-DAPM controls that the CS4270 supports */
static const struct snd_kcontrol_new cs4270_snd_controls[] = {
@@ -525,14 +498,19 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1)
};
+static const struct i2c_device_id cs4270_id[] = {
+ {"cs4270", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs4270_id);
+
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
.name = "CS4270 I2C",
.owner = THIS_MODULE,
},
- .id = I2C_DRIVERID_CS4270,
- .attach_adapter = cs4270_i2c_attach,
- .detach_client = cs4270_i2c_detach,
+ .id_table = cs4270_id,
+ .probe = cs4270_i2c_probe,
};
/*
@@ -561,11 +539,11 @@ static struct snd_soc_device *cs4270_socdev;
* Note: snd_soc_new_pcms() must be called before this function can be called,
* because of snd_ctl_add().
*/
-static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)
+static int cs4270_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = cs4270_socdev;
struct snd_soc_codec *codec = socdev->codec;
- struct i2c_client *i2c_client = NULL;
int i;
int ret = 0;
@@ -578,12 +556,6 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)
/* Note: codec_dai->codec is NULL here */
- i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!i2c_client) {
- printk(KERN_ERR "cs4270: could not allocate I2C client\n");
- return -ENOMEM;
- }
-
codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL);
if (!codec->reg_cache) {
printk(KERN_ERR "cs4270: could not allocate register cache\n");
@@ -591,13 +563,6 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)
goto error;
}
- i2c_set_clientdata(i2c_client, codec);
- strcpy(i2c_client->name, "CS4270");
-
- i2c_client->driver = &cs4270_i2c_driver;
- i2c_client->adapter = adapter;
- i2c_client->addr = addr;
-
/* Verify that we have a CS4270 */
ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
@@ -612,18 +577,10 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)
goto error;
}
- printk(KERN_INFO "cs4270: found device at I2C address %X\n", addr);
+ printk(KERN_INFO "cs4270: found device at I2C address %X\n",
+ i2c_client->addr);
printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF);
- /* Tell the I2C layer a new client has arrived */
-
- ret = i2c_attach_client(i2c_client);
- if (ret) {
- printk(KERN_ERR "cs4270: could not attach codec, "
- "I2C address %x, error code %i\n", addr, ret);
- goto error;
- }
-
codec->control_data = i2c_client;
codec->read = cs4270_read_reg_cache;
codec->write = cs4270_i2c_write;
@@ -648,26 +605,23 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)
goto error;
}
+ i2c_set_clientdata(i2c_client, codec);
+
return 0;
error:
- if (codec->control_data) {
- i2c_detach_client(i2c_client);
- codec->control_data = NULL;
- }
+ codec->control_data = NULL;
kfree(codec->reg_cache);
codec->reg_cache = NULL;
codec->reg_cache_size = 0;
- kfree(i2c_client);
-
return ret;
}
#endif /* USE_I2C*/
-struct snd_soc_codec_dai cs4270_dai = {
+struct snd_soc_dai cs4270_dai = {
.name = "CS4270",
.playback = {
.stream_name = "Playback",
@@ -727,7 +681,7 @@ static int cs4270_probe(struct platform_device *pdev)
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to create PCMs\n");
- return ret;
+ goto error_free_codec;
}
#ifdef USE_I2C
@@ -736,8 +690,7 @@ static int cs4270_probe(struct platform_device *pdev)
ret = i2c_add_driver(&cs4270_i2c_driver);
if (ret) {
printk(KERN_ERR "cs4270: failed to attach driver");
- snd_soc_free_pcms(socdev);
- return ret;
+ goto error_free_pcms;
}
/* Did we find a CS4270 on the I2C bus? */
@@ -759,10 +712,23 @@ static int cs4270_probe(struct platform_device *pdev)
ret = snd_soc_register_card(socdev);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to register card\n");
- snd_soc_free_pcms(socdev);
- return ret;
+ goto error_del_driver;
}
+ return 0;
+
+error_del_driver:
+#ifdef USE_I2C
+ i2c_del_driver(&cs4270_i2c_driver);
+
+error_free_pcms:
+#endif
+ snd_soc_free_pcms(socdev);
+
+error_free_codec:
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+
return ret;
}
@@ -773,8 +739,7 @@ static int cs4270_remove(struct platform_device *pdev)
snd_soc_free_pcms(socdev);
#ifdef USE_I2C
- if (socdev->codec->control_data)
- i2c_del_driver(&cs4270_i2c_driver);
+ i2c_del_driver(&cs4270_i2c_driver);
#endif
kfree(socdev->codec);
diff --git a/sound/soc/codecs/cs4270.h b/sound/soc/codecs/cs4270.h
index 0ced49b7804..adc6cd9667d 100644
--- a/sound/soc/codecs/cs4270.h
+++ b/sound/soc/codecs/cs4270.h
@@ -16,7 +16,7 @@
* The ASoC codec DAI structure for the CS4270. Assign this structure to
* the .codec_dai field of your machine driver's snd_soc_dai_link structure.
*/
-extern struct snd_soc_codec_dai cs4270_dai;
+extern struct snd_soc_dai cs4270_dai;
/*
* The ASoC codec device structure for the CS4270. Assign this structure
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
new file mode 100644
index 00000000000..44ef0dacd56
--- /dev/null
+++ b/sound/soc/codecs/ssm2602.c
@@ -0,0 +1,775 @@
+/*
+ * File: sound/soc/codecs/ssm2602.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ * Description: Driver for ssm2602 sound chip
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <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 "ssm2602.h"
+
+#define SSM2602_VERSION "0.1"
+
+struct snd_soc_codec_device soc_codec_dev_ssm2602;
+
+/* codec private data */
+struct ssm2602_priv {
+ unsigned int sysclk;
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+/*
+ * ssm2602 register cache
+ * We can't read the ssm2602 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
+ 0x0017, 0x0017, 0x0079, 0x0079,
+ 0x0000, 0x0000, 0x0000, 0x000a,
+ 0x0000, 0x0000
+};
+
+/*
+ * read ssm2602 register cache
+ */
+static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == SSM2602_RESET)
+ return 0;
+ if (reg >= SSM2602_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write ssm2602 register cache
+ */
+static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= SSM2602_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the ssm2602 register space
+ */
+static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 ssm2602 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ ssm2602_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0)
+
+/*Appending several "None"s just for OSS mixer use*/
+static const char *ssm2602_input_select[] = {
+ "Line", "Mic", "None", "None", "None",
+ "None", "None", "None",
+};
+
+static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum ssm2602_enum[] = {
+ SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select),
+ SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
+};
+
+static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+
+SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 0, 127, 0),
+SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 7, 1, 0),
+
+SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
+
+SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
+
+SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
+
+SOC_ENUM("Capture Source", ssm2602_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
+};
+
+/* add non dapm controls */
+static int ssm2602_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Output Mixer */
+static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ssm2602_input_mux_controls =
+SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
+
+static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
+ &ssm2602_output_mixer_controls[0],
+ ARRAY_SIZE(ssm2602_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
+SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const struct snd_soc_dapm_route audio_conn[] = {
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+ /* outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ /* input mux */
+ {"Input Mux", "Line", "Line Input"},
+ {"Input Mux", "Mic", "Mic Bias"},
+ {"ADC", NULL, "Input Mux"},
+
+ /* inputs */
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+ {"Mic Bias", NULL, "MICIN"},
+};
+
+static int ssm2602_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ssm2602_dapm_widgets,
+ ARRAY_SIZE(ssm2602_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 bosr:1;
+ u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
+ {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+ /* 8k */
+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return i;
+}
+
+static int ssm2602_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ u16 srate;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct ssm2602_priv *ssm2602 = codec->private_data;
+ u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
+ int i = get_coeff(ssm2602->sysclk, params_rate(params));
+
+ /*no match is found*/
+ if (i == ARRAY_SIZE(coeff_div))
+ return -EINVAL;
+
+ srate = (coeff_div[i].sr << 2) |
+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+ ssm2602_write(codec, SSM2602_ACTIVE, 0);
+ ssm2602_write(codec, SSM2602_SRATE, srate);
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0008;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x000c;
+ break;
+ }
+ ssm2602_write(codec, SSM2602_IFACE, iface);
+ ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+ return 0;
+}
+
+static int ssm2602_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct snd_pcm_runtime *master_runtime;
+
+ /* The DAI has shared clocks so if we already have a playback or
+ * capture going then constrain this substream to match it.
+ */
+ if (ssm2602->master_substream) {
+ master_runtime = ssm2602->master_substream->runtime;
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ master_runtime->rate,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ master_runtime->sample_bits,
+ master_runtime->sample_bits);
+
+ ssm2602->slave_substream = substream;
+ } else
+ ssm2602->master_substream = substream;
+
+ return 0;
+}
+
+static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ /* set active */
+ ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+
+ return 0;
+}
+
+static void ssm2602_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ /* deactivate */
+ if (!codec->active)
+ ssm2602_write(codec, SSM2602_ACTIVE, 0);
+}
+
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
+ if (mute)
+ ssm2602_write(codec, SSM2602_APDIGI,
+ mute_reg | APDIGI_ENABLE_DAC_MUTE);
+ else
+ ssm2602_write(codec, SSM2602_APDIGI, mute_reg);
+ return 0;
+}
+
+static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ssm2602_priv *ssm2602 = codec->private_data;
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ ssm2602->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ ssm2602_write(codec, SSM2602_IFACE, iface);
+ return 0;
+}
+
+static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* vref/mid, osc on, dac unmute */
+ ssm2602_write(codec, SSM2602_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ ssm2602_write(codec, SSM2602_ACTIVE, 0);
+ ssm2602_write(codec, SSM2602_PWR, 0xffff);
+ break;
+
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define SSM2602_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_dai ssm2602_dai = {
+ .name = "SSM2602",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SSM2602_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SSM2602_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+ .ops = {
+ .startup = ssm2602_startup,
+ .prepare = ssm2602_pcm_prepare,
+ .hw_params = ssm2602_hw_params,
+ .shutdown = ssm2602_shutdown,
+ },
+ .dai_ops = {
+ .digital_mute = ssm2602_mute,
+ .set_sysclk = ssm2602_set_dai_sysclk,
+ .set_fmt = ssm2602_set_dai_fmt,
+ }
+};
+EXPORT_SYMBOL_GPL(ssm2602_dai);
+
+static int ssm2602_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;
+
+ ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ssm2602_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ ssm2602_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the ssm2602 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ssm2602_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg, ret = 0;
+
+ codec->name = "SSM2602";
+ codec->owner = THIS_MODULE;
+ codec->read = ssm2602_read_reg_cache;
+ codec->write = ssm2602_write;
+ codec->set_bias_level = ssm2602_set_bias_level;
+ codec->dai = &ssm2602_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = sizeof(ssm2602_reg);
+ codec->reg_cache = kmemdup(ssm2602_reg, sizeof(ssm2602_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ ssm2602_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ pr_err("ssm2602: failed to create pcms\n");
+ goto pcm_err;
+ }
+ /*power on device*/
+ ssm2602_write(codec, SSM2602_ACTIVE, 0);
+ /* set the update bits */
+ reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL);
+ ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
+ reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL);
+ ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
+ reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V);
+ ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
+ reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
+ ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+ /*select Line in as default input*/
+ ssm2602_write(codec, SSM2602_APANA,
+ APANA_ENABLE_MIC_BOOST2 | APANA_SELECT_DAC |
+ APANA_ENABLE_MIC_BOOST);
+ ssm2602_write(codec, SSM2602_PWR, 0);
+
+ ssm2602_add_controls(codec);
+ ssm2602_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ pr_err("ssm2602: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *ssm2602_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/*
+ * ssm2602 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static int ssm2602_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = ssm2602_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = ssm2602_init(socdev);
+ if (ret < 0)
+ pr_err("failed to initialise SSM2602\n");
+
+ return ret;
+}
+
+static int ssm2602_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id ssm2602_i2c_id[] = {
+ { "ssm2602", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
+/* corgi i2c codec control layer */
+static struct i2c_driver ssm2602_i2c_driver = {
+ .driver = {
+ .name = "SSM2602 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2602_i2c_probe,
+ .remove = ssm2602_i2c_remove,
+ .id_table = ssm2602_i2c_id,
+};
+
+static int ssm2602_add_i2c_device(struct platform_device *pdev,
+ const struct ssm2602_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&ssm2602_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "ssm2602", I2C_NAME_SIZE);
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+ return 0;
+err_driver:
+ i2c_del_driver(&ssm2602_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+static int ssm2602_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct ssm2602_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct ssm2602_priv *ssm2602;
+ int ret = 0;
+
+ pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
+ if (ssm2602 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = ssm2602;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ssm2602_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = ssm2602_add_i2c_device(pdev, setup);
+ }
+#else
+ /* other interfaces */
+#endif
+ return ret;
+}
+
+/* remove everything here */
+static int ssm2602_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&ssm2602_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
+ .probe = ssm2602_probe,
+ .remove = ssm2602_remove,
+ .suspend = ssm2602_suspend,
+ .resume = ssm2602_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
+
+MODULE_DESCRIPTION("ASoC ssm2602 driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
new file mode 100644
index 00000000000..f344e6d76e3
--- /dev/null
+++ b/sound/soc/codecs/ssm2602.h
@@ -0,0 +1,130 @@
+/*
+ * File: sound/soc/codecs/ssm2602.h
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _SSM2602_H
+#define _SSM2602_H
+
+/* SSM2602 Codec Register definitions */
+
+#define SSM2602_LINVOL 0x00
+#define SSM2602_RINVOL 0x01
+#define SSM2602_LOUT1V 0x02
+#define SSM2602_ROUT1V 0x03
+#define SSM2602_APANA 0x04
+#define SSM2602_APDIGI 0x05
+#define SSM2602_PWR 0x06
+#define SSM2602_IFACE 0x07
+#define SSM2602_SRATE 0x08
+#define SSM2602_ACTIVE 0x09
+#define SSM2602_RESET 0x0f
+
+/*SSM2602 Codec Register Field definitions
+ *(Mask value to extract the corresponding Register field)
+ */
+
+/*Left ADC Volume Control (SSM2602_REG_LEFT_ADC_VOL)*/
+#define LINVOL_LIN_VOL 0x01F /* Left Channel PGA Volume control */
+#define LINVOL_LIN_ENABLE_MUTE 0x080 /* Left Channel Input Mute */
+#define LINVOL_LRIN_BOTH 0x100 /* Left Channel Line Input Volume update */
+
+/*Right ADC Volume Control (SSM2602_REG_RIGHT_ADC_VOL)*/
+#define RINVOL_RIN_VOL 0x01F /* Right Channel PGA Volume control */
+#define RINVOL_RIN_ENABLE_MUTE 0x080 /* Right Channel Input Mute */
+#define RINVOL_RLIN_BOTH 0x100 /* Right Channel Line Input Volume update */
+
+/*Left DAC Volume Control (SSM2602_REG_LEFT_DAC_VOL)*/
+#define LOUT1V_LHP_VOL 0x07F /* Left Channel Headphone volume control */
+#define LOUT1V_ENABLE_LZC 0x080 /* Left Channel Zero cross detect enable */
+#define LOUT1V_LRHP_BOTH 0x100 /* Left Channel Headphone volume update */
+
+/*Right DAC Volume Control (SSM2602_REG_RIGHT_DAC_VOL)*/
+#define ROUT1V_RHP_VOL 0x07F /* Right Channel Headphone volume control */
+#define ROUT1V_ENABLE_RZC 0x080 /* Right Channel Zero cross detect enable */
+#define ROUT1V_RLHP_BOTH 0x100 /* Right Channel Headphone volume update */
+
+/*Analogue Audio Path Control (SSM2602_REG_ANALOGUE_PATH)*/
+#define APANA_ENABLE_MIC_BOOST 0x001 /* Primary Microphone Amplifier gain booster control */
+#define APANA_ENABLE_MIC_MUTE 0x002 /* Microphone Mute Control */
+#define APANA_ADC_IN_SELECT 0x004 /* Microphone/Line IN select to ADC (1=MIC, 0=Line In) */
+#define APANA_ENABLE_BYPASS 0x008 /* Line input bypass to line output */
+#define APANA_SELECT_DAC 0x010 /* Select DAC (1=Select DAC, 0=Don't Select DAC) */
+#define APANA_ENABLE_SIDETONE 0x020 /* Enable/Disable Side Tone */
+#define APANA_SIDETONE_ATTN 0x0C0 /* Side Tone Attenuation */
+#define APANA_ENABLE_MIC_BOOST2 0x100 /* Secondary Microphone Amplifier gain booster control */
+
+/*Digital Audio Path Control (SSM2602_REG_DIGITAL_PATH)*/
+#define APDIGI_ENABLE_ADC_HPF 0x001 /* Enable/Disable ADC Highpass Filter */
+#define APDIGI_DE_EMPHASIS 0x006 /* De-Emphasis Control */
+#define APDIGI_ENABLE_DAC_MUTE 0x008 /* DAC Mute Control */
+#define APDIGI_STORE_OFFSET 0x010 /* Store/Clear DC offset when HPF is disabled */
+
+/*Power Down Control (SSM2602_REG_POWER)
+ *(1=Enable PowerDown, 0=Disable PowerDown)
+ */
+#define PWR_LINE_IN_PDN 0x001 /* Line Input Power Down */
+#define PWR_MIC_PDN 0x002 /* Microphone Input & Bias Power Down */
+#define PWR_ADC_PDN 0x004 /* ADC Power Down */
+#define PWR_DAC_PDN 0x008 /* DAC Power Down */
+#define PWR_OUT_PDN 0x010 /* Outputs Power Down */
+#define PWR_OSC_PDN 0x020 /* Oscillator Power Down */
+#define PWR_CLK_OUT_PDN 0x040 /* CLKOUT Power Down */
+#define PWR_POWER_OFF 0x080 /* POWEROFF Mode */
+
+/*Digital Audio Interface Format (SSM2602_REG_DIGITAL_IFACE)*/
+#define IFACE_IFACE_FORMAT 0x003 /* Digital Audio input format control */
+#define IFACE_AUDIO_DATA_LEN 0x00C /* Audio Data word length control */
+#define IFACE_DAC_LR_POLARITY 0x010 /* Polarity Control for clocks in RJ,LJ and I2S modes */
+#define IFACE_DAC_LR_SWAP 0x020 /* Swap DAC data control */
+#define IFACE_ENABLE_MASTER 0x040 /* Enable/Disable Master Mode */
+#define IFACE_BCLK_INVERT 0x080 /* Bit Clock Inversion control */
+
+/*Sampling Control (SSM2602_REG_SAMPLING_CTRL)*/
+#define SRATE_ENABLE_USB_MODE 0x001 /* Enable/Disable USB Mode */
+#define SRATE_BOS_RATE 0x002 /* Base Over-Sampling rate */
+#define SRATE_SAMPLE_RATE 0x03C /* Clock setting condition (Sampling rate control) */
+#define SRATE_CORECLK_DIV2 0x040 /* Core Clock divider select */
+#define SRATE_CLKOUT_DIV2 0x080 /* Clock Out divider select */
+
+/*Active Control (SSM2602_REG_ACTIVE_CTRL)*/
+#define ACTIVE_ACTIVATE_CODEC 0x001 /* Activate Codec Digital Audio Interface */
+
+/*********************************************************************/
+
+#define SSM2602_CACHEREGNUM 10
+
+#define SSM2602_SYSCLK 0
+#define SSM2602_DAI 0
+
+struct ssm2602_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ssm2602_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ssm2602;
+
+#endif
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
new file mode 100644
index 00000000000..44308dac9e1
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -0,0 +1,714 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.c by Richard Purdie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Notes:
+ * The AIC23 is a driver for a low power stereo audio
+ * codec tlv320aic23
+ *
+ * The machine layer should disable unsupported inputs/outputs by
+ * snd_soc_dapm_disable_pin(codec, "LHPOUT"), 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/tlv.h>
+#include <sound/initval.h>
+
+#include "tlv320aic23.h"
+
+#define AIC23_VERSION "0.1"
+
+struct tlv320aic23_srate_reg_info {
+ u32 sample_rate;
+ u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
+ u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
+};
+
+/*
+ * AIC23 register cache
+ */
+static const u16 tlv320aic23_reg[] = {
+ 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */
+ 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */
+ 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */
+};
+
+/*
+ * read tlv320aic23 register cache
+ */
+static inline unsigned int tlv320aic23_read_reg_cache(struct snd_soc_codec
+ *codec, unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= ARRAY_SIZE(tlv320aic23_reg))
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write tlv320aic23 register cache
+ */
+static inline void tlv320aic23_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u16 value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= ARRAY_SIZE(tlv320aic23_reg))
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the tlv320aic23 register space
+ */
+static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+
+ u8 data[2];
+
+ /* TLV320AIC23 has 7 bit address and 9 bits of data
+ * so we need to switch one data bit into reg and rest
+ * of data into val
+ */
+
+ if ((reg < 0 || reg > 9) && (reg != 15)) {
+ printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg);
+ return -1;
+ }
+
+ data[0] = (reg << 1) | (value >> 8 & 0x01);
+ data[1] = value & 0xff;
+
+ tlv320aic23_write_reg_cache(codec, reg, value);
+
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+
+ printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__,
+ value, reg);
+
+ return -EIO;
+}
+
+static const char *rec_src_text[] = { "Line", "Mic" };
+static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum rec_src_enum =
+ SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+
+static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls =
+SOC_DAPM_ENUM("Input Select", rec_src_enum);
+
+static const struct soc_enum tlv320aic23_rec_src =
+ SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+static const struct soc_enum tlv320aic23_deemph =
+ SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0);
+
+static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 val, reg;
+
+ val = (ucontrol->value.integer.value[0] & 0x07);
+
+ /* linear conversion to userspace
+ * 000 = -6db
+ * 001 = -9db
+ * 010 = -12db
+ * 011 = -18db (Min)
+ * 100 = 0db (Max)
+ */
+ val = (val >= 4) ? 4 : (3 - val);
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (~0x1C0);
+ tlv320aic23_write(codec, TLV320AIC23_ANLG, reg | (val << 6));
+
+ return 0;
+}
+
+static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 val;
+
+ val = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (0x1C0);
+ val = val >> 6;
+ val = (val >= 4) ? 4 : (3 - val);
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+
+}
+
+#define SOC_TLV320AIC23_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, .get = snd_soc_tlv320aic23_get_volsw,\
+ .put = snd_soc_tlv320aic23_put_volsw, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL,
+ TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv),
+ SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1),
+ SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv),
+ SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1),
+ SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0),
+ SOC_TLV320AIC23_SINGLE_TLV("Sidetone Volume", TLV320AIC23_ANLG,
+ 6, 4, 0, sidetone_vol_tlv),
+ SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
+};
+
+/* add non dapm controls */
+static int tlv320aic23_add_controls(struct snd_soc_codec *codec)
+{
+
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&tlv320aic23_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+
+}
+
+/* PGA Mixer controls for Line and Mic switch */
+static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1),
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &tlv320aic23_rec_src_mux_controls),
+ SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1,
+ &tlv320aic23_output_mixer_controls[0],
+ ARRAY_SIZE(tlv320aic23_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Output Mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Input"},
+
+ /* Outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+
+ /* Inputs */
+ {"Line Input", "NULL", "LLINEIN"},
+ {"Line Input", "NULL", "RLINEIN"},
+
+ {"Mic Input", "NULL", "MICIN"},
+
+ /* input mux */
+ {"Capture Source", "Line", "Line Input"},
+ {"Capture Source", "Mic", "Mic Input"},
+ {"ADC", NULL, "Capture Source"},
+
+};
+
+/* tlv320aic23 related */
+static const struct tlv320aic23_srate_reg_info srate_reg_info[] = {
+ {4000, 0x06, 1}, /* 4000 */
+ {8000, 0x06, 0}, /* 8000 */
+ {16000, 0x0C, 1}, /* 16000 */
+ {22050, 0x11, 1}, /* 22050 */
+ {24000, 0x00, 1}, /* 24000 */
+ {32000, 0x0C, 0}, /* 32000 */
+ {44100, 0x11, 0}, /* 44100 */
+ {48000, 0x00, 0}, /* 48000 */
+ {88200, 0x1F, 0}, /* 88200 */
+ {96000, 0x0E, 0}, /* 96000 */
+};
+
+static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int tlv320aic23_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;
+ u16 iface_reg, data;
+ u8 count = 0;
+
+ iface_reg =
+ tlv320aic23_read_reg_cache(codec,
+ TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
+
+ /* Search for the right sample rate */
+ /* Verify what happens if the rate is not supported
+ * now it goes to 96Khz */
+ while ((srate_reg_info[count].sample_rate != params_rate(params)) &&
+ (count < ARRAY_SIZE(srate_reg_info))) {
+ count++;
+ }
+
+ data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) |
+ (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) |
+ TLV320AIC23_USB_CLK_ON;
+
+ tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface_reg |= (0x01 << 2);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface_reg |= (0x02 << 2);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface_reg |= (0x03 << 2);
+ break;
+ }
+ tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* set active */
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* deactivate */
+ if (!codec->active) {
+ udelay(50);
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ }
+}
+
+static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT);
+ if (mute)
+ reg |= TLV320AIC23_DACM_MUTE;
+
+ else
+ reg &= ~TLV320AIC23_DACM_MUTE;
+
+ tlv320aic23_write(codec, TLV320AIC23_DIGT, reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface_reg;
+
+ iface_reg =
+ tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT_FMT) & (~0x03);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface_reg |= TLV320AIC23_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg |= TLV320AIC23_FOR_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg |= TLV320AIC23_FOR_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg |= TLV320AIC23_FOR_LJUST;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ switch (freq) {
+ case 12000000:
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_PWR) & 0xff7f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* vref/mid, osc on, dac unmute */
+ tlv320aic23_write(codec, TLV320AIC23_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ tlv320aic23_write(codec, TLV320AIC23_PWR, 0xffff);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define AIC23_RATES SNDRV_PCM_RATE_8000_96000
+#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai tlv320aic23_dai = {
+ .name = "tlv320aic23",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .ops = {
+ .prepare = tlv320aic23_pcm_prepare,
+ .hw_params = tlv320aic23_hw_params,
+ .shutdown = tlv320aic23_shutdown,
+ },
+ .dai_ops = {
+ .digital_mute = tlv320aic23_mute,
+ .set_fmt = tlv320aic23_set_dai_fmt,
+ .set_sysclk = tlv320aic23_set_dai_sysclk,
+ }
+};
+EXPORT_SYMBOL_GPL(tlv320aic23_dai);
+
+static int tlv320aic23_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;
+
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int tlv320aic23_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u16 reg;
+
+ /* Sync reg_cache with the hardware */
+ for (reg = 0; reg < ARRAY_SIZE(tlv320aic23_reg); i++) {
+ u16 val = tlv320aic23_read_reg_cache(codec, reg);
+ tlv320aic23_write(codec, reg, val);
+ }
+
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ tlv320aic23_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+/*
+ * initialise the AIC23 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int tlv320aic23_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+ u16 reg;
+
+ codec->name = "tlv320aic23";
+ codec->owner = THIS_MODULE;
+ codec->read = tlv320aic23_read_reg_cache;
+ codec->write = tlv320aic23_write;
+ codec->set_bias_level = tlv320aic23_set_bias_level;
+ codec->dai = &tlv320aic23_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(tlv320aic23_reg);
+ codec->reg_cache =
+ kmemdup(tlv320aic23_reg, sizeof(tlv320aic23_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* Reset codec */
+ tlv320aic23_write(codec, TLV320AIC23_RESET, 0);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "tlv320aic23: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ tlv320aic23_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
+
+ /* Unmute input */
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_LINVOL);
+ tlv320aic23_write(codec, TLV320AIC23_LINVOL,
+ (reg & (~TLV320AIC23_LIM_MUTED)) |
+ (TLV320AIC23_LRS_ENABLED));
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_RINVOL);
+ tlv320aic23_write(codec, TLV320AIC23_RINVOL,
+ (reg & (~TLV320AIC23_LIM_MUTED)) |
+ TLV320AIC23_LRS_ENABLED);
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG);
+ tlv320aic23_write(codec, TLV320AIC23_ANLG,
+ (reg) & (~TLV320AIC23_BYPASS_ON) &
+ (~TLV320AIC23_MICM_MUTED));
+
+ /* Default output volume */
+ tlv320aic23_write(codec, TLV320AIC23_LCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL &
+ TLV320AIC23_OUT_VOL_MASK);
+ tlv320aic23_write(codec, TLV320AIC23_RCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL &
+ TLV320AIC23_OUT_VOL_MASK);
+
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1);
+
+ tlv320aic23_add_controls(codec);
+ tlv320aic23_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "tlv320aic23: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+static struct snd_soc_device *tlv320aic23_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/*
+ * If the i2c layer weren't so broken, we could pass this kind of data
+ * around
+ */
+static int tlv320aic23_codec_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ struct snd_soc_device *socdev = tlv320aic23_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EINVAL;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = tlv320aic23_init(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "tlv320aic23: failed to initialise AIC23\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c)
+{
+ put_device(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id tlv320aic23_id[] = {
+ {"tlv320aic23", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
+
+static struct i2c_driver tlv320aic23_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic23",
+ },
+ .probe = tlv320aic23_codec_probe,
+ .remove = __exit_p(tlv320aic23_i2c_remove),
+ .id_table = tlv320aic23_id,
+};
+
+#endif
+
+static int tlv320aic23_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ tlv320aic23_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ codec->hw_read = NULL;
+ ret = i2c_add_driver(&tlv320aic23_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+#endif
+ return ret;
+}
+
+static int tlv320aic23_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&tlv320aic23_i2c_driver);
+#endif
+ kfree(codec->reg_cache);
+ kfree(codec);
+
+ return 0;
+}
+struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = {
+ .probe = tlv320aic23_probe,
+ .remove = tlv320aic23_remove,
+ .suspend = tlv320aic23_suspend,
+ .resume = tlv320aic23_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h
new file mode 100644
index 00000000000..79d1faf8e57
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic23.h
@@ -0,0 +1,122 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd
+ *
+ * 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 _TLV320AIC23_H
+#define _TLV320AIC23_H
+
+/* Codec TLV320AIC23 */
+#define TLV320AIC23_LINVOL 0x00
+#define TLV320AIC23_RINVOL 0x01
+#define TLV320AIC23_LCHNVOL 0x02
+#define TLV320AIC23_RCHNVOL 0x03
+#define TLV320AIC23_ANLG 0x04
+#define TLV320AIC23_DIGT 0x05
+#define TLV320AIC23_PWR 0x06
+#define TLV320AIC23_DIGT_FMT 0x07
+#define TLV320AIC23_SRATE 0x08
+#define TLV320AIC23_ACTIVE 0x09
+#define TLV320AIC23_RESET 0x0F
+
+/* Left (right) line input volume control register */
+#define TLV320AIC23_LRS_ENABLED 0x0100
+#define TLV320AIC23_LIM_MUTED 0x0080
+#define TLV320AIC23_LIV_DEFAULT 0x0017
+#define TLV320AIC23_LIV_MAX 0x001f
+#define TLV320AIC23_LIV_MIN 0x0000
+
+/* Left (right) channel headphone volume control register */
+#define TLV320AIC23_LZC_ON 0x0080
+#define TLV320AIC23_LHV_DEFAULT 0x0079
+#define TLV320AIC23_LHV_MAX 0x007f
+#define TLV320AIC23_LHV_MIN 0x0000
+
+/* Analog audio path control register */
+#define TLV320AIC23_STA_REG(x) ((x)<<6)
+#define TLV320AIC23_STE_ENABLED 0x0020
+#define TLV320AIC23_DAC_SELECTED 0x0010
+#define TLV320AIC23_BYPASS_ON 0x0008
+#define TLV320AIC23_INSEL_MIC 0x0004
+#define TLV320AIC23_MICM_MUTED 0x0002
+#define TLV320AIC23_MICB_20DB 0x0001
+
+/* Digital audio path control register */
+#define TLV320AIC23_DACM_MUTE 0x0008
+#define TLV320AIC23_DEEMP_32K 0x0002
+#define TLV320AIC23_DEEMP_44K 0x0004
+#define TLV320AIC23_DEEMP_48K 0x0006
+#define TLV320AIC23_ADCHP_ON 0x0001
+
+/* Power control down register */
+#define TLV320AIC23_DEVICE_PWR_OFF 0x0080
+#define TLV320AIC23_CLK_OFF 0x0040
+#define TLV320AIC23_OSC_OFF 0x0020
+#define TLV320AIC23_OUT_OFF 0x0010
+#define TLV320AIC23_DAC_OFF 0x0008
+#define TLV320AIC23_ADC_OFF 0x0004
+#define TLV320AIC23_MIC_OFF 0x0002
+#define TLV320AIC23_LINE_OFF 0x0001
+
+/* Digital audio interface register */
+#define TLV320AIC23_MS_MASTER 0x0040
+#define TLV320AIC23_LRSWAP_ON 0x0020
+#define TLV320AIC23_LRP_ON 0x0010
+#define TLV320AIC23_IWL_16 0x0000
+#define TLV320AIC23_IWL_20 0x0004
+#define TLV320AIC23_IWL_24 0x0008
+#define TLV320AIC23_IWL_32 0x000C
+#define TLV320AIC23_FOR_I2S 0x0002
+#define TLV320AIC23_FOR_DSP 0x0003
+#define TLV320AIC23_FOR_LJUST 0x0001
+
+/* Sample rate control register */
+#define TLV320AIC23_CLKOUT_HALF 0x0080
+#define TLV320AIC23_CLKIN_HALF 0x0040
+#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */
+#define TLV320AIC23_USB_CLK_ON 0x0001
+#define TLV320AIC23_SR_MASK 0xf
+#define TLV320AIC23_CLKOUT_SHIFT 7
+#define TLV320AIC23_CLKIN_SHIFT 6
+#define TLV320AIC23_SR_SHIFT 2
+#define TLV320AIC23_BOSR_SHIFT 1
+
+/* Digital interface register */
+#define TLV320AIC23_ACT_ON 0x0001
+
+/*
+ * AUDIO related MACROS
+ */
+
+#define TLV320AIC23_DEFAULT_OUT_VOL 0x70
+#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10
+
+#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN
+#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX
+#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \
+ TLV320AIC23_OUT_VOL_MIN)
+#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX
+
+#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN
+#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX
+#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \
+ TLV320AIC23_IN_VOL_MIN)
+#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX
+
+#define TLV320AIC23_SIDETONE_MASK 0x1c0
+#define TLV320AIC23_SIDETONE_0 0x100
+#define TLV320AIC23_SIDETONE_6 0x000
+#define TLV320AIC23_SIDETONE_9 0x040
+#define TLV320AIC23_SIDETONE_12 0x080
+#define TLV320AIC23_SIDETONE_18 0x0c0
+
+extern struct snd_soc_dai tlv320aic23_dai;
+extern struct snd_soc_codec_device soc_codec_dev_tlv320aic23;
+
+#endif /* _TLV320AIC23_H */
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
new file mode 100644
index 00000000000..bed8a9e63dd
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -0,0 +1,520 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * ALSA SoC CODEC driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-of-simple.h>
+#include <sound/initval.h>
+
+#include "tlv320aic26.h"
+
+MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+/* AIC26 driver private data */
+struct aic26 {
+ struct spi_device *spi;
+ struct snd_soc_codec codec;
+ u16 reg_cache[AIC26_NUM_REGS]; /* shadow registers */
+ int master;
+ int datfm;
+ int mclk;
+
+ /* Keyclick parameters */
+ int keyclick_amplitude;
+ int keyclick_freq;
+ int keyclick_len;
+};
+
+/* ---------------------------------------------------------------------
+ * Register access routines
+ */
+static unsigned int aic26_reg_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct aic26 *aic26 = codec->private_data;
+ u16 *cache = codec->reg_cache;
+ u16 cmd, value;
+ u8 buffer[2];
+ int rc;
+
+ if (reg >= AIC26_NUM_REGS) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ /* Do SPI transfer; first 16bits are command; remaining is
+ * register contents */
+ cmd = AIC26_READ_COMMAND_WORD(reg);
+ buffer[0] = (cmd >> 8) & 0xff;
+ buffer[1] = cmd & 0xff;
+ rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2);
+ if (rc) {
+ dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
+ return -EIO;
+ }
+ value = (buffer[0] << 8) | buffer[1];
+
+ /* Update the cache before returning with the value */
+ cache[reg] = value;
+ return value;
+}
+
+static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= AIC26_NUM_REGS) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ return cache[reg];
+}
+
+static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct aic26 *aic26 = codec->private_data;
+ u16 *cache = codec->reg_cache;
+ u16 cmd;
+ u8 buffer[4];
+ int rc;
+
+ if (reg >= AIC26_NUM_REGS) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ /* Do SPI transfer; first 16bits are command; remaining is data
+ * to write into register */
+ cmd = AIC26_WRITE_COMMAND_WORD(reg);
+ buffer[0] = (cmd >> 8) & 0xff;
+ buffer[1] = cmd & 0xff;
+ buffer[2] = value >> 8;
+ buffer[3] = value;
+ rc = spi_write(aic26->spi, buffer, 4);
+ if (rc) {
+ dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
+ return -EIO;
+ }
+
+ /* update cache before returning */
+ cache[reg] = value;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Operations
+ */
+static int aic26_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 aic26 *aic26 = codec->private_data;
+ int fsref, divisor, wlen, pval, jval, dval, qval;
+ u16 reg;
+
+ dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n",
+ substream, params);
+ dev_dbg(&aic26->spi->dev, "rate=%i format=%i\n", params_rate(params),
+ params_format(params));
+
+ switch (params_rate(params)) {
+ case 8000: fsref = 48000; divisor = AIC26_DIV_6; break;
+ case 11025: fsref = 44100; divisor = AIC26_DIV_4; break;
+ case 12000: fsref = 48000; divisor = AIC26_DIV_4; break;
+ case 16000: fsref = 48000; divisor = AIC26_DIV_3; break;
+ case 22050: fsref = 44100; divisor = AIC26_DIV_2; break;
+ case 24000: fsref = 48000; divisor = AIC26_DIV_2; break;
+ case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break;
+ case 44100: fsref = 44100; divisor = AIC26_DIV_1; break;
+ case 48000: fsref = 48000; divisor = AIC26_DIV_1; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL;
+ }
+
+ /* select data word length */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8: wlen = AIC26_WLEN_16; break;
+ case SNDRV_PCM_FORMAT_S16_BE: wlen = AIC26_WLEN_16; break;
+ case SNDRV_PCM_FORMAT_S24_BE: wlen = AIC26_WLEN_24; break;
+ case SNDRV_PCM_FORMAT_S32_BE: wlen = AIC26_WLEN_32; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
+
+ /* Configure PLL */
+ pval = 1;
+ jval = (fsref == 44100) ? 7 : 8;
+ dval = (fsref == 44100) ? 5264 : 1920;
+ qval = 0;
+ reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
+ aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
+ reg = dval << 2;
+ aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
+
+ /* Audio Control 3 (master mode, fsref rate) */
+ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
+ reg &= ~0xf800;
+ if (aic26->master)
+ reg |= 0x0800;
+ if (fsref == 48000)
+ reg |= 0x2000;
+ aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+ /* Audio Control 1 (FSref divisor) */
+ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
+ reg &= ~0x0fff;
+ reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
+ aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
+
+ return 0;
+}
+
+/**
+ * aic26_mute - Mute control to reduce noise when changing audio format
+ */
+static int aic26_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic26 *aic26 = codec->private_data;
+ u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN);
+
+ dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
+ dai, mute);
+
+ if (mute)
+ reg |= 0x8080;
+ else
+ reg &= ~0x8080;
+ aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
+
+ return 0;
+}
+
+static int aic26_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic26 *aic26 = codec->private_data;
+
+ dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
+ " freq=%i, dir=%i)\n",
+ codec_dai, clk_id, freq, dir);
+
+ /* MCLK needs to fall between 2MHz and 50 MHz */
+ if ((freq < 2000000) || (freq > 50000000))
+ return -EINVAL;
+
+ aic26->mclk = freq;
+ return 0;
+}
+
+static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic26 *aic26 = codec->private_data;
+
+ dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
+ codec_dai, fmt);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break;
+ case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break;
+ case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break;
+ case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
+ case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Definition
+ */
+#define AIC26_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+struct snd_soc_dai aic26_dai = {
+ .name = "tlv320aic26",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC26_RATES,
+ .formats = AIC26_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC26_RATES,
+ .formats = AIC26_FORMATS,
+ },
+ .ops = {
+ .hw_params = aic26_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = aic26_mute,
+ .set_sysclk = aic26_set_sysclk,
+ .set_fmt = aic26_set_fmt,
+ },
+};
+EXPORT_SYMBOL_GPL(aic26_dai);
+
+/* ---------------------------------------------------------------------
+ * ALSA controls
+ */
+static const char *aic26_capture_src_text[] = {"Mic", "Aux"};
+static const struct soc_enum aic26_capture_src_enum =
+ SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12, 2, aic26_capture_src_text);
+
+static const struct snd_kcontrol_new aic26_snd_controls[] = {
+ /* Output */
+ SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1),
+ SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1),
+ SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0),
+ SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1),
+ SOC_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0),
+ SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0),
+ SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0),
+ SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0),
+ SOC_ENUM("Capture Source", aic26_capture_src_enum),
+};
+
+/* ---------------------------------------------------------------------
+ * SoC CODEC portion of driver: probe and release routines
+ */
+static int aic26_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct snd_kcontrol *kcontrol;
+ struct aic26 *aic26;
+ int i, ret, err;
+
+ dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n");
+ dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
+ dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data);
+
+ /* Fetch the relevant aic26 private data here (it's already been
+ * stored in the .codec pointer) */
+ aic26 = socdev->codec_data;
+ if (aic26 == NULL) {
+ dev_err(&pdev->dev, "aic26: missing codec pointer\n");
+ return -ENODEV;
+ }
+ codec = &aic26->codec;
+ socdev->codec = codec;
+
+ dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
+ &pdev->dev, socdev->dev);
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "aic26: failed to create pcms\n");
+ return -ENODEV;
+ }
+
+ /* register controls */
+ dev_dbg(&pdev->dev, "Registering controls\n");
+ for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) {
+ kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL);
+ err = snd_ctl_add(codec->card, kcontrol);
+ WARN_ON(err < 0);
+ }
+
+ /* CODEC is setup, we can register the card now */
+ dev_dbg(&pdev->dev, "Registering card\n");
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "aic26: failed to register card\n");
+ goto card_err;
+ }
+ return 0;
+
+ card_err:
+ snd_soc_free_pcms(socdev);
+ return ret;
+}
+
+static int aic26_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ snd_soc_free_pcms(socdev);
+ return 0;
+}
+
+struct snd_soc_codec_device aic26_soc_codec_dev = {
+ .probe = aic26_probe,
+ .remove = aic26_remove,
+};
+EXPORT_SYMBOL_GPL(aic26_soc_codec_dev);
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: sysfs files for debugging
+ */
+
+static ssize_t aic26_keyclick_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+ int val, amp, freq, len;
+
+ val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ amp = (val >> 12) & 0x7;
+ freq = (125 << ((val >> 8) & 0x7)) >> 1;
+ len = 2 * (1 + ((val >> 4) & 0xf));
+
+ return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+}
+
+/* Any write to the keyclick attribute will trigger the keyclick event */
+static ssize_t aic26_keyclick_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+ int val;
+
+ val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val |= 0x8000;
+ aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
+
+ return count;
+}
+
+static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: probe and release routines and SPI
+ * driver registration.
+ */
+static int aic26_spi_probe(struct spi_device *spi)
+{
+ struct aic26 *aic26;
+ int rc, i, reg;
+
+ dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
+
+ /* Allocate driver data */
+ aic26 = kzalloc(sizeof *aic26, GFP_KERNEL);
+ if (!aic26)
+ return -ENOMEM;
+
+ /* Initialize the driver data */
+ aic26->spi = spi;
+ dev_set_drvdata(&spi->dev, aic26);
+
+ /* Setup what we can in the codec structure so that the register
+ * access functions will work as expected. More will be filled
+ * out when it is probed by the SoC CODEC part of this driver */
+ aic26->codec.private_data = aic26;
+ aic26->codec.name = "aic26";
+ aic26->codec.owner = THIS_MODULE;
+ aic26->codec.dai = &aic26_dai;
+ aic26->codec.num_dai = 1;
+ aic26->codec.read = aic26_reg_read;
+ aic26->codec.write = aic26_reg_write;
+ aic26->master = 1;
+ mutex_init(&aic26->codec.mutex);
+ INIT_LIST_HEAD(&aic26->codec.dapm_widgets);
+ INIT_LIST_HEAD(&aic26->codec.dapm_paths);
+ aic26->codec.reg_cache_size = AIC26_NUM_REGS;
+ aic26->codec.reg_cache = aic26->reg_cache;
+
+ /* Reset the codec to power on defaults */
+ aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
+
+ /* Power up CODEC */
+ aic26_reg_write(&aic26->codec, AIC26_REG_POWER_CTRL, 0);
+
+ /* Audio Control 3 (master mode, fsref rate) */
+ reg = aic26_reg_read(&aic26->codec, AIC26_REG_AUDIO_CTRL3);
+ reg &= ~0xf800;
+ reg |= 0x0800; /* set master mode */
+ aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+ /* Fill register cache */
+ for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++)
+ aic26_reg_read(&aic26->codec, i);
+
+ /* Register the sysfs files for debugging */
+ /* Create SysFS files */
+ rc = device_create_file(&spi->dev, &dev_attr_keyclick);
+ if (rc)
+ dev_info(&spi->dev, "error creating sysfs files\n");
+
+#if defined(CONFIG_SND_SOC_OF_SIMPLE)
+ /* Tell the of_soc helper about this codec */
+ of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
+ spi->dev.archdata.of_node);
+#endif
+
+ dev_dbg(&spi->dev, "SPI device initialized\n");
+ return 0;
+}
+
+static int aic26_spi_remove(struct spi_device *spi)
+{
+ struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
+
+ kfree(aic26);
+
+ return 0;
+}
+
+static struct spi_driver aic26_spi = {
+ .driver = {
+ .name = "tlv320aic26",
+ .owner = THIS_MODULE,
+ },
+ .probe = aic26_spi_probe,
+ .remove = aic26_spi_remove,
+};
+
+static int __init aic26_init(void)
+{
+ return spi_register_driver(&aic26_spi);
+}
+module_init(aic26_init);
+
+static void __exit aic26_exit(void)
+{
+ spi_unregister_driver(&aic26_spi);
+}
+module_exit(aic26_exit);
diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h
new file mode 100644
index 00000000000..786ba16c945
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.h
@@ -0,0 +1,96 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * register definitions
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#ifndef _TLV320AIC16_H_
+#define _TLV320AIC16_H_
+
+/* AIC26 Registers */
+#define AIC26_READ_COMMAND_WORD(addr) ((1 << 15) | (addr << 5))
+#define AIC26_WRITE_COMMAND_WORD(addr) ((0 << 15) | (addr << 5))
+#define AIC26_PAGE_ADDR(page, offset) ((page << 6) | offset)
+#define AIC26_NUM_REGS AIC26_PAGE_ADDR(3, 0)
+
+/* Page 0: Auxillary data registers */
+#define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05)
+#define AIC26_REG_BAT2 AIC26_PAGE_ADDR(0, 0x06)
+#define AIC26_REG_AUX AIC26_PAGE_ADDR(0, 0x07)
+#define AIC26_REG_TEMP1 AIC26_PAGE_ADDR(0, 0x09)
+#define AIC26_REG_TEMP2 AIC26_PAGE_ADDR(0, 0x0A)
+
+/* Page 1: Auxillary control registers */
+#define AIC26_REG_AUX_ADC AIC26_PAGE_ADDR(1, 0x00)
+#define AIC26_REG_STATUS AIC26_PAGE_ADDR(1, 0x01)
+#define AIC26_REG_REFERENCE AIC26_PAGE_ADDR(1, 0x03)
+#define AIC26_REG_RESET AIC26_PAGE_ADDR(1, 0x04)
+
+/* Page 2: Audio control registers */
+#define AIC26_REG_AUDIO_CTRL1 AIC26_PAGE_ADDR(2, 0x00)
+#define AIC26_REG_ADC_GAIN AIC26_PAGE_ADDR(2, 0x01)
+#define AIC26_REG_DAC_GAIN AIC26_PAGE_ADDR(2, 0x02)
+#define AIC26_REG_SIDETONE AIC26_PAGE_ADDR(2, 0x03)
+#define AIC26_REG_AUDIO_CTRL2 AIC26_PAGE_ADDR(2, 0x04)
+#define AIC26_REG_POWER_CTRL AIC26_PAGE_ADDR(2, 0x05)
+#define AIC26_REG_AUDIO_CTRL3 AIC26_PAGE_ADDR(2, 0x06)
+
+#define AIC26_REG_FILTER_COEFF_L_N0 AIC26_PAGE_ADDR(2, 0x07)
+#define AIC26_REG_FILTER_COEFF_L_N1 AIC26_PAGE_ADDR(2, 0x08)
+#define AIC26_REG_FILTER_COEFF_L_N2 AIC26_PAGE_ADDR(2, 0x09)
+#define AIC26_REG_FILTER_COEFF_L_N3 AIC26_PAGE_ADDR(2, 0x0A)
+#define AIC26_REG_FILTER_COEFF_L_N4 AIC26_PAGE_ADDR(2, 0x0B)
+#define AIC26_REG_FILTER_COEFF_L_N5 AIC26_PAGE_ADDR(2, 0x0C)
+#define AIC26_REG_FILTER_COEFF_L_D1 AIC26_PAGE_ADDR(2, 0x0D)
+#define AIC26_REG_FILTER_COEFF_L_D2 AIC26_PAGE_ADDR(2, 0x0E)
+#define AIC26_REG_FILTER_COEFF_L_D4 AIC26_PAGE_ADDR(2, 0x0F)
+#define AIC26_REG_FILTER_COEFF_L_D5 AIC26_PAGE_ADDR(2, 0x10)
+#define AIC26_REG_FILTER_COEFF_R_N0 AIC26_PAGE_ADDR(2, 0x11)
+#define AIC26_REG_FILTER_COEFF_R_N1 AIC26_PAGE_ADDR(2, 0x12)
+#define AIC26_REG_FILTER_COEFF_R_N2 AIC26_PAGE_ADDR(2, 0x13)
+#define AIC26_REG_FILTER_COEFF_R_N3 AIC26_PAGE_ADDR(2, 0x14)
+#define AIC26_REG_FILTER_COEFF_R_N4 AIC26_PAGE_ADDR(2, 0x15)
+#define AIC26_REG_FILTER_COEFF_R_N5 AIC26_PAGE_ADDR(2, 0x16)
+#define AIC26_REG_FILTER_COEFF_R_D1 AIC26_PAGE_ADDR(2, 0x17)
+#define AIC26_REG_FILTER_COEFF_R_D2 AIC26_PAGE_ADDR(2, 0x18)
+#define AIC26_REG_FILTER_COEFF_R_D4 AIC26_PAGE_ADDR(2, 0x19)
+#define AIC26_REG_FILTER_COEFF_R_D5 AIC26_PAGE_ADDR(2, 0x1A)
+
+#define AIC26_REG_PLL_PROG1 AIC26_PAGE_ADDR(2, 0x1B)
+#define AIC26_REG_PLL_PROG2 AIC26_PAGE_ADDR(2, 0x1C)
+#define AIC26_REG_AUDIO_CTRL4 AIC26_PAGE_ADDR(2, 0x1D)
+#define AIC26_REG_AUDIO_CTRL5 AIC26_PAGE_ADDR(2, 0x1E)
+
+/* fsref dividers; used in register 'Audio Control 1' */
+enum aic26_divisors {
+ AIC26_DIV_1 = 0,
+ AIC26_DIV_1_5 = 1,
+ AIC26_DIV_2 = 2,
+ AIC26_DIV_3 = 3,
+ AIC26_DIV_4 = 4,
+ AIC26_DIV_5 = 5,
+ AIC26_DIV_5_5 = 6,
+ AIC26_DIV_6 = 7,
+};
+
+/* Digital data format */
+enum aic26_datfm {
+ AIC26_DATFM_I2S = 0 << 8,
+ AIC26_DATFM_DSP = 1 << 8,
+ AIC26_DATFM_RIGHTJ = 2 << 8, /* right justified */
+ AIC26_DATFM_LEFTJ = 3 << 8, /* left justified */
+};
+
+/* Sample word length in bits; used in register 'Audio Control 1' */
+enum aic26_wlen {
+ AIC26_WLEN_16 = 0 << 10,
+ AIC26_WLEN_20 = 1 << 10,
+ AIC26_WLEN_24 = 2 << 10,
+ AIC26_WLEN_32 = 3 << 10,
+};
+
+extern struct snd_soc_dai aic26_dai;
+extern struct snd_soc_codec_device aic26_soc_codec_dev;
+
+#endif /* _TLV320AIC16_H_ */
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 09b1661b8a3..05336ed7e49 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1,7 +1,7 @@
/*
* ALSA SoC TLV320AIC3X codec driver
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* Based on sound/soc/codecs/wm8753.c by Liam Girdwood
@@ -29,7 +29,7 @@
* ---------------------------------------
*
* Hence the machine layer should disable unsupported inputs/outputs by
- * snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
+ * snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc.
*/
#include <linux/module.h>
@@ -48,8 +48,7 @@
#include "tlv320aic3x.h"
-#define AUDIO_NAME "aic3x"
-#define AIC3X_VERSION "0.1"
+#define AIC3X_VERSION "0.2"
/* codec private data */
struct aic3x_priv {
@@ -138,6 +137,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
+/*
+ * read from the aic3x register space
+ */
+static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
+ u8 *value)
+{
+ *value = reg & 0xff;
+ if (codec->hw_read(codec->control_data, value, 1) != 1)
+ return -EIO;
+
+ aic3x_write_reg_cache(codec, reg, *value);
+ return 0;
+}
+
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \
@@ -192,7 +205,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
}
if (found)
- snd_soc_dapm_sync_endpoints(widget->codec);
+ snd_soc_dapm_sync(widget->codec);
}
ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
@@ -209,6 +222,8 @@ 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" };
+static const char *aic3x_adc_hpf[] =
+ { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
#define LDAC_ENUM 0
#define RDAC_ENUM 1
@@ -218,6 +233,7 @@ static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
#define LINE1R_ENUM 5
#define LINE2L_ENUM 6
#define LINE2R_ENUM 7
+#define ADC_HPF_ENUM 8
static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@@ -228,6 +244,7 @@ static const struct soc_enum aic3x_enum[] = {
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),
+ SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
};
static const struct snd_kcontrol_new aic3x_snd_controls[] = {
@@ -278,6 +295,8 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
/* 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),
+
+ SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
};
/* add non dapm controls */
@@ -441,11 +460,34 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line2_mux_controls),
+ /*
+ * Not a real mic bias widget but similar function. This is for dynamic
+ * control of GPIO1 digital mic modulator clock output function when
+ * using digital mic.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk",
+ AIC3X_GPIO1_REG, 4, 0xf,
+ AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK,
+ AIC3X_GPIO1_FUNC_DISABLED),
+
+ /*
+ * Also similar function like mic bias. Selects digital mic with
+ * configurable oversampling rate instead of ADC converter.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
+
/* 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),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
+ MICBIAS_CTRL, 6, 3, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
+ MICBIAS_CTRL, 6, 3, 2, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
+ MICBIAS_CTRL, 6, 3, 3, 0),
/* Left PGA to Left Output bypass */
SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
@@ -483,7 +525,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINE2R"),
};
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
/* Left Output */
{"Left DAC Mux", "DAC_L1", "Left DAC"},
{"Left DAC Mux", "DAC_L2", "Left DAC"},
@@ -554,6 +596,7 @@ static const char *intercon[][3] = {
{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
{"Left ADC", NULL, "Left PGA Mixer"},
+ {"Left ADC", NULL, "GPIO1 dmic modclk"},
/* Right Input */
{"Right Line1R Mux", "single-ended", "LINE1R"},
@@ -567,6 +610,7 @@ static const char *intercon[][3] = {
{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
{"Right ADC", NULL, "Right PGA Mixer"},
+ {"Right ADC", NULL, "GPIO1 dmic modclk"},
/* Left PGA Bypass */
{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
@@ -628,101 +672,27 @@ static const char *intercon[][3] = {
{"Mono Out", NULL, "Right Line2 Bypass Mixer"},
{"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
- /* terminator */
- {NULL, NULL, NULL},
+ /*
+ * Logical path between digital mic enable and GPIO1 modulator clock
+ * output function
+ */
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 128"},
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 64"},
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
};
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]);
+ snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+ ARRAY_SIZE(aic3x_dapm_widgets));
/* 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_add_routes(codec, intercon, ARRAY_SIZE(intercon));
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 */
- {12000000, 8000, 48000, 0xa, 16, 3840},
- {19200000, 8000, 48000, 0xa, 10, 2400},
- {22579200, 8000, 48000, 0xa, 8, 7075},
- {33868800, 8000, 48000, 0xa, 5, 8049},
- /* 11.025k */
- {12000000, 11025, 44100, 0x6, 15, 528},
- {19200000, 11025, 44100, 0x6, 9, 4080},
- {22579200, 11025, 44100, 0x6, 8, 0},
- {33868800, 11025, 44100, 0x6, 5, 3333},
- /* 16k */
- {12000000, 16000, 48000, 0x4, 16, 3840},
- {19200000, 16000, 48000, 0x4, 10, 2400},
- {22579200, 16000, 48000, 0x4, 8, 7075},
- {33868800, 16000, 48000, 0x4, 5, 8049},
- /* 22.05k */
- {12000000, 22050, 44100, 0x2, 15, 528},
- {19200000, 22050, 44100, 0x2, 9, 4080},
- {22579200, 22050, 44100, 0x2, 8, 0},
- {33868800, 22050, 44100, 0x2, 5, 3333},
- /* 32k */
- {12000000, 32000, 48000, 0x1, 16, 3840},
- {19200000, 32000, 48000, 0x1, 10, 2400},
- {22579200, 32000, 48000, 0x1, 8, 7075},
- {33868800, 32000, 48000, 0x1, 5, 8049},
- /* 44.1k */
- {12000000, 44100, 44100, 0x0, 15, 528},
- {19200000, 44100, 44100, 0x0, 9, 4080},
- {22579200, 44100, 44100, 0x0, 8, 0},
- {33868800, 44100, 44100, 0x0, 5, 3333},
- /* 48k */
- {12000000, 48000, 48000, 0x0, 16, 3840},
- {19200000, 48000, 48000, 0x0, 10, 2400},
- {22579200, 48000, 48000, 0x0, 8, 7075},
- {33868800, 48000, 48000, 0x0, 5, 8049},
- /* 64k */
- {12000000, 64000, 96000, 0x1, 16, 3840},
- {19200000, 64000, 96000, 0x1, 10, 2400},
- {22579200, 64000, 96000, 0x1, 8, 7075},
- {33868800, 64000, 96000, 0x1, 5, 8049},
- /* 88.2k */
- {12000000, 88200, 88200, 0x0, 15, 528},
- {19200000, 88200, 88200, 0x0, 9, 4080},
- {22579200, 88200, 88200, 0x0, 8, 0},
- {33868800, 88200, 88200, 0x0, 5, 3333},
- /* 96k */
- {12000000, 96000, 96000, 0x0, 16, 3840},
- {19200000, 96000, 96000, 0x0, 10, 2400},
- {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)
{
@@ -730,49 +700,107 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
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;
+ int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
+ u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+ u16 pll_d = 1;
- 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;
+ /* 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 48000:
- data |= FSREF_48000;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ data |= (0x01 << 4);
break;
- case 88200:
- data |= FSREF_44100 | DUAL_RATE_MODE;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ data |= (0x02 << 4);
break;
- case 96000:
- data |= FSREF_48000 | DUAL_RATE_MODE;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ data |= (0x03 << 4);
break;
}
+ aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+
+ /* Fsref can be 44100 or 48000 */
+ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+
+ /* Try to find a value for Q which allows us to bypass the PLL and
+ * generate CODEC_CLK directly. */
+ for (pll_q = 2; pll_q < 18; pll_q++)
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
+
+ if (bypass_pll) {
+ pll_q &= 0xf;
+ aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+ aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+ } else
+ aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+
+ /* Route Left DAC to left channel input and
+ * right DAC to right channel input */
+ data = (LDAC2LCH | RDAC2RCH);
+ data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
+ if (params_rate(params) >= 64000)
+ data |= DUAL_RATE_MODE;
aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
- data = aic3x_divs[i].sr_reg;
+ data = (fsref * 20) / params_rate(params);
+ if (params_rate(params) < 64000)
+ data /= 2;
+ data /= 5;
+ data -= 2;
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
+ if (bypass_pll)
+ return 0;
+
+ /* Use PLL
+ * find an apropriate setup for j, d, r and p by iterating over
+ * p and r - j and d are calculated for each fraction.
+ * Up to 128 values are probed, the closest one wins the game.
+ * The sysclk is divided by 1000 to prevent integer overflows.
*/
- pll_p = 2;
- pll_r = 1;
- pll_j = aic3x_divs[i].pllj_reg;
- pll_d = aic3x_divs[i].plld_reg;
+ codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
+
+ for (r = 1; r <= 16; r++)
+ for (p = 1; p <= 8; p++) {
+ int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
+ u8 j = tmp / 10000;
+ u16 d = tmp % 10000;
+
+ if (j > 63)
+ continue;
+
+ if (d != 0 && aic3x->sysclk < 10000000)
+ continue;
+
+ /* This is actually 1000 * ((j + (d/10000)) * r) / p
+ * The term had to be converted to get rid of the
+ * division by 10000 */
+ clk = ((10000 * j * r) + (d * r)) / (10 * p);
+
+ /* check whether this values get closer than the best
+ * ones we had before */
+ if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+ pll_j = j; pll_d = d; pll_r = r; pll_p = p;
+ last_clk = clk;
+ }
+
+ /* Early exit for exact matches */
+ if (clk == codec_clk)
+ break;
+ }
+
+ if (last_clk == 0) {
+ printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
+ return -EINVAL;
+ }
data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
@@ -782,28 +810,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
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)
+static int aic3x_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
@@ -820,31 +830,25 @@ static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct aic3x_priv *aic3x = codec->private_data;
- switch (freq) {
- case 12000000:
- case 19200000:
- case 22579200:
- case 33868800:
- aic3x->sysclk = freq;
- return 0;
- }
-
- return -EINVAL;
+ aic3x->sysclk = freq;
+ return 0;
}
-static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_fmt(struct snd_soc_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;
+ u8 iface_areg, iface_breg;
+
+ iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+ iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -883,13 +887,14 @@ static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
+static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
struct aic3x_priv *aic3x = codec->private_data;
u8 reg;
- switch (event) {
- case SNDRV_CTL_POWER_D0:
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* all power is driven by DAPM system */
if (aic3x->master) {
/* enable pll */
@@ -898,10 +903,9 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
reg | PLL_ENABLE);
}
break;
- case SNDRV_CTL_POWER_D1:
- case SNDRV_CTL_POWER_D2:
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot:
+ case SND_SOC_BIAS_STANDBY:
/*
* all power is driven by DAPM system,
* so output power is safe if bypass was set
@@ -913,7 +917,7 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
reg & ~PLL_ENABLE);
}
break;
- case SNDRV_CTL_POWER_D3cold:
+ case SND_SOC_BIAS_OFF:
/* 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);
@@ -949,17 +953,44 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
}
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
+{
+ u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+ u8 bit = gpio ? 3: 0;
+ u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
+ aic3x_write(codec, reg, val | (!!state << bit));
+}
+EXPORT_SYMBOL_GPL(aic3x_set_gpio);
+
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
+{
+ u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+ u8 val, bit = gpio ? 2: 1;
+
+ aic3x_read(codec, reg, &val);
+ return (val >> bit) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_get_gpio);
+
+int aic3x_headset_detected(struct snd_soc_codec *codec)
+{
+ u8 val;
+ aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
+ return (val >> 2) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_headset_detected);
+
#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",
+struct snd_soc_dai aic3x_dai = {
+ .name = "tlv320aic3x",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -988,7 +1019,7 @@ 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);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1008,7 +1039,7 @@ static int aic3x_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- aic3x_dapm_event(codec, codec->suspend_dapm_state);
+ aic3x_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -1020,16 +1051,17 @@ static int aic3x_resume(struct platform_device *pdev)
static int aic3x_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
+ struct aic3x_setup_data *setup = socdev->codec_data;
int reg, ret = 0;
- codec->name = "aic3x";
+ codec->name = "tlv320aic3x";
codec->owner = THIS_MODULE;
codec->read = aic3x_read_reg_cache;
codec->write = aic3x_write;
- codec->dapm_event = aic3x_dapm_event;
+ codec->set_bias_level = aic3x_set_bias_level;
codec->dai = &aic3x_dai;
codec->num_dai = 1;
- codec->reg_cache_size = sizeof(aic3x_reg);
+ codec->reg_cache_size = ARRAY_SIZE(aic3x_reg);
codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
@@ -1108,7 +1140,11 @@ static int aic3x_init(struct snd_soc_device *socdev)
aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
/* off, with power on */
- aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* setup GPIO functions */
+ aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
+ aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
aic3x_add_controls(codec);
aic3x_add_widgets(codec);
@@ -1135,73 +1171,39 @@ static struct snd_soc_device *aic3x_socdev;
* 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)
+static int aic3x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
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) {
+ 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)
+static int aic3x_i2c_remove(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);
-}
+static const struct i2c_device_id aic3x_i2c_id[] = {
+ { "tlv320aic3x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
/* machine i2c codec control layer */
static struct i2c_driver aic3x_i2c_driver = {
@@ -1209,14 +1211,56 @@ static struct i2c_driver aic3x_i2c_driver = {
.name = "aic3x I2C Codec",
.owner = THIS_MODULE,
},
- .attach_adapter = aic3x_i2c_attach,
- .detach_client = aic3x_i2c_detach,
+ .probe = aic3x_i2c_probe,
+ .remove = aic3x_i2c_remove,
+ .id_table = aic3x_i2c_id,
};
-static struct i2c_client client_template = {
- .name = "AIC3X",
- .driver = &aic3x_i2c_driver,
-};
+static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
+{
+ value[0] = i2c_smbus_read_byte_data(client, value[0]);
+ return (len == 1);
+}
+
+static int aic3x_add_i2c_device(struct platform_device *pdev,
+ const struct aic3x_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&aic3x_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&aic3x_i2c_driver);
+ return -ENODEV;
+}
#endif
static int aic3x_probe(struct platform_device *pdev)
@@ -1249,15 +1293,18 @@ static int aic3x_probe(struct platform_device *pdev)
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");
+ codec->hw_read = (hw_read_t) aic3x_i2c_read;
+ ret = aic3x_add_i2c_device(pdev, setup);
}
#else
/* Add other interfaces here */
#endif
+
+ if (ret != 0) {
+ kfree(codec->private_data);
+ kfree(codec);
+ }
return ret;
}
@@ -1268,11 +1315,12 @@ static int aic3x_remove(struct platform_device *pdev)
/* power down chip */
if (codec->control_data)
- aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
i2c_del_driver(&aic3x_i2c_driver);
#endif
kfree(codec->private_data);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index d0cdeeb629d..00a195aa02e 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -1,7 +1,7 @@
/*
* ALSA SoC TLV320AIC3X codec driver
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -37,6 +37,8 @@
#define AIC3X_ASD_INTF_CTRLB 9
/* Audio overflow status and PLL R value programming register */
#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
+/* Audio codec digital filter control register */
+#define AIC3X_CODEC_DFILT_CTRL 12
/* ADC PGA Gain control registers */
#define LADC_VOL 15
@@ -108,6 +110,13 @@
#define DACR1_2_RLOPM_VOL 92
#define LLOPM_CTRL 86
#define RLOPM_CTRL 93
+/* GPIO/IRQ registers */
+#define AIC3X_STICKY_IRQ_FLAGS_REG 96
+#define AIC3X_RT_IRQ_FLAGS_REG 97
+#define AIC3X_GPIO1_REG 98
+#define AIC3X_GPIO2_REG 99
+#define AIC3X_GPIOA_REG 100
+#define AIC3X_GPIOB_REG 101
/* Clock generation control register */
#define AIC3X_CLKGEN_CTRL_REG 102
@@ -128,12 +137,15 @@
/* PLL registers bitfields */
#define PLLP_SHIFT 0
+#define PLLQ_SHIFT 3
#define PLLR_SHIFT 0
#define PLLJ_SHIFT 2
#define PLLD_MSB_SHIFT 0
#define PLLD_LSB_SHIFT 2
/* Clock generation register bits */
+#define CODEC_CLKIN_PLLDIV 0
+#define CODEC_CLKIN_CLKDIV 1
#define PLL_CLKIN_SHIFT 4
#define MCLK_SOURCE 0x0
#define PLL_CLKDIV_SHIFT 0
@@ -171,11 +183,53 @@
/* Default input volume */
#define DEFAULT_GAIN 0x20
+/* GPIO API */
+enum {
+ AIC3X_GPIO1_FUNC_DISABLED = 0,
+ AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC = 1,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX = 2,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2 = 3,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4 = 4,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8 = 5,
+ AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ = 6,
+ AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ = 7,
+ AIC3X_GPIO1_FUNC_INPUT = 8,
+ AIC3X_GPIO1_FUNC_OUTPUT = 9,
+ AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK = 10,
+ AIC3X_GPIO1_FUNC_AUDIO_WORDCLK = 11,
+ AIC3X_GPIO1_FUNC_BUTTON_IRQ = 12,
+ AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ = 13,
+ AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 14,
+ AIC3X_GPIO1_FUNC_ALL_IRQ = 16
+};
+
+enum {
+ AIC3X_GPIO2_FUNC_DISABLED = 0,
+ AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ = 2,
+ AIC3X_GPIO2_FUNC_INPUT = 3,
+ AIC3X_GPIO2_FUNC_OUTPUT = 4,
+ AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT = 5,
+ AIC3X_GPIO2_FUNC_AUDIO_BITCLK = 8,
+ AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9,
+ AIC3X_GPIO2_FUNC_ALL_IRQ = 10,
+ AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11,
+ AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12,
+ AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ = 13,
+ AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ = 14,
+ AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15
+};
+
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+int aic3x_headset_detected(struct snd_soc_codec *codec);
+
struct aic3x_setup_data {
+ int i2c_bus;
unsigned short i2c_address;
+ unsigned int gpio_func[2];
};
-extern struct snd_soc_codec_dai aic3x_dai;
+extern struct snd_soc_dai aic3x_dai;
extern struct snd_soc_codec_device soc_codec_dev_aic3x;
#endif /* _AIC3X_H */
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
new file mode 100644
index 00000000000..a69ee72a7af
--- /dev/null
+++ b/sound/soc/codecs/uda1380.c
@@ -0,0 +1,849 @@
+/*
+ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
+ *
+ * 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.
+ *
+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
+ * Improved support for DAPM and audio routing/mixing capabilities,
+ * added TLV support.
+ *
+ * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
+ * codec model.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright 2005 Openedhand Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "uda1380.h"
+
+#define UDA1380_VERSION "0.6"
+
+/*
+ * uda1380 register cache
+ */
+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
+ 0x0502, 0x0000, 0x0000, 0x3f3f,
+ 0x0202, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xff00, 0x0000, 0x4800,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x8000, 0x0002, 0x0000,
+};
+
+/*
+ * read uda1380 register cache
+ */
+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == UDA1380_RESET)
+ return 0;
+ if (reg >= UDA1380_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write uda1380 register cache
+ */
+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= UDA1380_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the UDA1380 register space
+ */
+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ /* data is
+ * data[0] is register offset
+ * data[1] is MS byte
+ * data[2] is LS byte
+ */
+ data[0] = reg;
+ data[1] = (value & 0xff00) >> 8;
+ data[2] = value & 0x00ff;
+
+ uda1380_write_reg_cache(codec, reg, value);
+
+ /* the interpolator & decimator regs must only be written when the
+ * codec DAI is active.
+ */
+ if (!codec->active && (reg >= UDA1380_MVOL))
+ return 0;
+ pr_debug("uda1380: hw write %x val %x\n", reg, value);
+ if (codec->hw_write(codec->control_data, data, 3) == 3) {
+ unsigned int val;
+ i2c_master_send(codec->control_data, data, 1);
+ i2c_master_recv(codec->control_data, data, 2);
+ val = (data[0]<<8) | data[1];
+ if (val != value) {
+ pr_debug("uda1380: READ BACK VAL %x\n",
+ (data[0]<<8) | data[1]);
+ return -EIO;
+ }
+ return 0;
+ } else
+ return -EIO;
+}
+
+#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
+
+/* declarations of ALSA reg_elem_REAL controls */
+static const char *uda1380_deemp[] = {
+ "None",
+ "32kHz",
+ "44.1kHz",
+ "48kHz",
+ "96kHz",
+};
+static const char *uda1380_input_sel[] = {
+ "Line",
+ "Mic + Line R",
+ "Line L",
+ "Mic",
+};
+static const char *uda1380_output_sel[] = {
+ "DAC",
+ "Analog Mixer",
+};
+static const char *uda1380_spf_mode[] = {
+ "Flat",
+ "Minimum1",
+ "Minimum2",
+ "Maximum"
+};
+static const char *uda1380_capture_sel[] = {
+ "ADC",
+ "Digital Mixer"
+};
+static const char *uda1380_sel_ns[] = {
+ "3rd-order",
+ "5th-order"
+};
+static const char *uda1380_mix_control[] = {
+ "off",
+ "PCM only",
+ "before sound processing",
+ "after sound processing"
+};
+static const char *uda1380_sdet_setting[] = {
+ "3200",
+ "4800",
+ "9600",
+ "19200"
+};
+static const char *uda1380_os_setting[] = {
+ "single-speed",
+ "double-speed (no mixing)",
+ "quad-speed (no mixing)"
+};
+
+static const struct soc_enum uda1380_deemp_enum[] = {
+ SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
+ SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
+};
+static const struct soc_enum uda1380_input_sel_enum =
+ SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel); /* SEL_MIC, SEL_LNA */
+static const struct soc_enum uda1380_output_sel_enum =
+ SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel); /* R02_EN_AVC */
+static const struct soc_enum uda1380_spf_enum =
+ SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode); /* M */
+static const struct soc_enum uda1380_capture_sel_enum =
+ SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel); /* SEL_SOURCE */
+static const struct soc_enum uda1380_sel_ns_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns); /* SEL_NS */
+static const struct soc_enum uda1380_mix_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control); /* MIX, MIX_POS */
+static const struct soc_enum uda1380_sdet_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting); /* SD_VALUE */
+static const struct soc_enum uda1380_os_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting); /* OS */
+
+/*
+ * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
+
+/*
+ * from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored),
+ * from -66 dB in 0.5 dB steps (2 dB steps, really) and
+ * from -52 dB in 0.25 dB steps
+ */
+static const unsigned int mvol_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
+ 16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
+ 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
+};
+
+/*
+ * from -72 dB in 1.5 dB steps (6 dB steps really),
+ * from -66 dB in 0.75 dB steps (3 dB steps really),
+ * from -60 dB in 0.5 dB steps (2 dB steps really) and
+ * from -46 dB in 0.25 dB steps
+ */
+static const unsigned int vc_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
+ 16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
+ 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
+};
+
+/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
+static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
+
+/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts
+ * off at 18 dB max) */
+static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);
+
+/* from -63 to 24 dB in 0.5 dB steps (-128...48) */
+static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1);
+
+/* from 0 to 24 dB in 3 dB steps */
+static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+/* from 0 to 30 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
+ SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv), /* AVCR, AVCL */
+ SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv), /* MVCL, MVCR */
+ SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv), /* VC2 */
+ SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv), /* VC1 */
+ SOC_ENUM("Sound Processing Filter", uda1380_spf_enum), /* M */
+ SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), /* TRL, TRR */
+ SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv), /* BBL, BBR */
+/**/ SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1), /* MTM */
+ SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1), /* MT2 from decimation filter */
+ SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]), /* DE2 */
+ SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1), /* MT1, from digital data input */
+ SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]), /* DE1 */
+ SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */
+ SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */
+ SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */
+ SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0), /* SILENCE, force DAC output to silence */
+ SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */
+ SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */
+ SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */
+ SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv), /* ML_DEC, MR_DEC */
+/**/ SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1), /* MT_ADC */
+ SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
+ SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0), /* ADCPOL_INV */
+ SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv), /* VGA_CTRL */
+ SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0), /* SKIP_DCFIL (before decimator) */
+ SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0), /* EN_DCFIL (at output of decimator) */
+ SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0), /* TODO: enum, see table 62 */
+ SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1), /* AGC_LEVEL */
+ /* -5.5, -8, -11.5, -14 dBFS */
+ SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
+};
+
+/* add non dapm controls */
+static int uda1380_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Input mux */
+static const struct snd_kcontrol_new uda1380_input_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
+
+/* Output mux */
+static const struct snd_kcontrol_new uda1380_output_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
+
+/* Capture mux */
+static const struct snd_kcontrol_new uda1380_capture_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
+
+
+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_input_mux_control),
+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_output_mux_control),
+ SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_capture_mux_control),
+ SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
+ SND_SOC_DAPM_INPUT("VINM"),
+ SND_SOC_DAPM_INPUT("VINL"),
+ SND_SOC_DAPM_INPUT("VINR"),
+ SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("VOUTLHP"),
+ SND_SOC_DAPM_OUTPUT("VOUTRHP"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
+ SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* output mux */
+ {"HeadPhone Driver", NULL, "Output Mux"},
+ {"VOUTR", NULL, "Output Mux"},
+ {"VOUTL", NULL, "Output Mux"},
+
+ {"Analog Mixer", NULL, "VINR"},
+ {"Analog Mixer", NULL, "VINL"},
+ {"Analog Mixer", NULL, "DAC"},
+
+ {"Output Mux", "DAC", "DAC"},
+ {"Output Mux", "Analog Mixer", "Analog Mixer"},
+
+ /* {"DAC", "Digital Mixer", "I2S" } */
+
+ /* headphone driver */
+ {"VOUTLHP", NULL, "HeadPhone Driver"},
+ {"VOUTRHP", NULL, "HeadPhone Driver"},
+
+ /* input mux */
+ {"Left ADC", NULL, "Input Mux"},
+ {"Input Mux", "Mic", "Mic LNA"},
+ {"Input Mux", "Mic + Line R", "Mic LNA"},
+ {"Input Mux", "Line L", "Left PGA"},
+ {"Input Mux", "Line", "Left PGA"},
+
+ /* right input */
+ {"Right ADC", "Mic + Line R", "Right PGA"},
+ {"Right ADC", "Line", "Right PGA"},
+
+ /* inputs */
+ {"Mic LNA", NULL, "VINM"},
+ {"Left PGA", NULL, "VINL"},
+ {"Right PGA", NULL, "VINR"},
+};
+
+static int uda1380_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
+ ARRAY_SIZE(uda1380_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
+
+ /* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORI_I2S | R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORI_MSB | R01_SFORO_I2S;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
+ iface |= R01_SIM;
+
+ uda1380_write(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+/*
+ * Flush reg cache
+ * We can only write the interpolator and decimator registers
+ * when the DAI is being clocked by the CPU DAI. It's up to the
+ * machine and cpu DAI driver to do this before we are called.
+ */
+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg, reg_start, reg_end, clk;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_start = UDA1380_MVOL;
+ reg_end = UDA1380_MIXER;
+ } else {
+ reg_start = UDA1380_DEC;
+ reg_end = UDA1380_AGC;
+ }
+
+ /* FIXME disable DAC_CLK */
+ clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+ uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
+
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ pr_debug("uda1380: flush reg %x val %x:", reg,
+ uda1380_read_reg_cache(codec, reg));
+ uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
+ }
+
+ /* FIXME enable DAC_CLK */
+ uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
+
+ return 0;
+}
+
+static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+ /* set WSPLL power and divider if running from this clock */
+ if (clk & R00_DAC_CLK) {
+ int rate = params_rate(params);
+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ clk &= ~0x3; /* clear SEL_LOOP_DIV */
+ switch (rate) {
+ case 6250 ... 12500:
+ clk |= 0x0;
+ break;
+ case 12501 ... 25000:
+ clk |= 0x1;
+ break;
+ case 25001 ... 50000:
+ clk |= 0x2;
+ break;
+ case 50001 ... 100000:
+ clk |= 0x3;
+ break;
+ }
+ uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk |= R00_EN_DAC | R00_EN_INT;
+ else
+ clk |= R00_EN_ADC | R00_EN_DEC;
+
+ uda1380_write(codec, UDA1380_CLK, clk);
+ return 0;
+}
+
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+ /* shut down WSPLL power if running from this clock */
+ if (clk & R00_DAC_CLK) {
+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk &= ~(R00_EN_DAC | R00_EN_INT);
+ else
+ clk &= ~(R00_EN_ADC | R00_EN_DEC);
+
+ uda1380_write(codec, UDA1380_CLK, clk);
+}
+
+static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
+
+ /* FIXME: mute(codec,0) is called when the magician clock is already
+ * set to WSPLL, but for some unknown reason writing to interpolator
+ * registers works only when clocked by SYSCLK */
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+ uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
+ if (mute)
+ uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
+ else
+ uda1380_write(codec, UDA1380_DEEMP, mute_reg);
+ uda1380_write(codec, UDA1380_CLK, clk);
+ return 0;
+}
+
+static int uda1380_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
+ break;
+ case SND_SOC_BIAS_OFF:
+ uda1380_write(codec, UDA1380_PM, 0x0);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai uda1380_dai[] = {
+{
+ .name = "UDA1380",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .prepare = uda1380_pcm_prepare,
+ },
+ .dai_ops = {
+ .digital_mute = uda1380_mute,
+ .set_fmt = uda1380_set_dai_fmt,
+ },
+},
+{ /* playback only - dual interface */
+ .name = "UDA1380",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .prepare = uda1380_pcm_prepare,
+ },
+ .dai_ops = {
+ .digital_mute = uda1380_mute,
+ .set_fmt = uda1380_set_dai_fmt,
+ },
+},
+{ /* capture only - dual interface*/
+ .name = "UDA1380",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .prepare = uda1380_pcm_prepare,
+ },
+ .dai_ops = {
+ .set_fmt = uda1380_set_dai_fmt,
+ },
+},
+};
+EXPORT_SYMBOL_GPL(uda1380_dai);
+
+static int uda1380_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;
+
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int uda1380_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ uda1380_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the UDA1380 driver
+ * register mixer and dsp interfaces with the kernel
+ */
+static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "UDA1380";
+ codec->owner = THIS_MODULE;
+ codec->read = uda1380_read_reg_cache;
+ codec->write = uda1380_write;
+ codec->set_bias_level = uda1380_set_bias_level;
+ codec->dai = uda1380_dai;
+ codec->num_dai = ARRAY_SIZE(uda1380_dai);
+ codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+ codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+ codec->reg_cache_step = 1;
+ uda1380_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ pr_err("uda1380: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* set clock input */
+ switch (dac_clk) {
+ case UDA1380_DAC_CLK_SYSCLK:
+ uda1380_write(codec, UDA1380_CLK, 0);
+ break;
+ case UDA1380_DAC_CLK_WSPLL:
+ uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
+ break;
+ }
+
+ /* uda1380 init */
+ uda1380_add_controls(codec);
+ uda1380_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ pr_err("uda1380: 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 *uda1380_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static int uda1380_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = uda1380_socdev;
+ struct uda1380_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = uda1380_init(socdev, setup->dac_clk);
+ if (ret < 0)
+ pr_err("uda1380: failed to initialise UDA1380\n");
+
+ return ret;
+}
+
+static int uda1380_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id uda1380_i2c_id[] = {
+ { "uda1380", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
+
+static struct i2c_driver uda1380_i2c_driver = {
+ .driver = {
+ .name = "UDA1380 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = uda1380_i2c_probe,
+ .remove = uda1380_i2c_remove,
+ .id_table = uda1380_i2c_id,
+};
+
+static int uda1380_add_i2c_device(struct platform_device *pdev,
+ const struct uda1380_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&uda1380_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "uda1380", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&uda1380_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+static int uda1380_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct uda1380_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret;
+
+ pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ uda1380_socdev = socdev;
+ ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = uda1380_add_i2c_device(pdev, setup);
+ }
+#endif
+
+ if (ret != 0)
+ kfree(codec);
+ return ret;
+}
+
+/* power down chip */
+static int uda1380_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&uda1380_i2c_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+ .probe = uda1380_probe,
+ .remove = uda1380_remove,
+ .suspend = uda1380_suspend,
+ .resume = uda1380_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+
+MODULE_AUTHOR("Giorgio Padrin");
+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
new file mode 100644
index 00000000000..c55c17a52a1
--- /dev/null
+++ b/sound/soc/codecs/uda1380.h
@@ -0,0 +1,90 @@
+/*
+ * Audio support for Philips UDA1380
+ *
+ * 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.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ */
+
+#ifndef _UDA1380_H
+#define _UDA1380_H
+
+#define UDA1380_CLK 0x00
+#define UDA1380_IFACE 0x01
+#define UDA1380_PM 0x02
+#define UDA1380_AMIX 0x03
+#define UDA1380_HP 0x04
+#define UDA1380_MVOL 0x10
+#define UDA1380_MIXVOL 0x11
+#define UDA1380_MODE 0x12
+#define UDA1380_DEEMP 0x13
+#define UDA1380_MIXER 0x14
+#define UDA1380_INTSTAT 0x18
+#define UDA1380_DEC 0x20
+#define UDA1380_PGA 0x21
+#define UDA1380_ADC 0x22
+#define UDA1380_AGC 0x23
+#define UDA1380_DECSTAT 0x28
+#define UDA1380_RESET 0x7f
+
+#define UDA1380_CACHEREGNUM 0x24
+
+/* Register flags */
+#define R00_EN_ADC 0x0800
+#define R00_EN_DEC 0x0400
+#define R00_EN_DAC 0x0200
+#define R00_EN_INT 0x0100
+#define R00_DAC_CLK 0x0010
+#define R01_SFORI_I2S 0x0000
+#define R01_SFORI_LSB16 0x0100
+#define R01_SFORI_LSB18 0x0200
+#define R01_SFORI_LSB20 0x0300
+#define R01_SFORI_MSB 0x0500
+#define R01_SFORI_MASK 0x0700
+#define R01_SFORO_I2S 0x0000
+#define R01_SFORO_LSB16 0x0001
+#define R01_SFORO_LSB18 0x0002
+#define R01_SFORO_LSB20 0x0003
+#define R01_SFORO_LSB24 0x0004
+#define R01_SFORO_MSB 0x0005
+#define R01_SFORO_MASK 0x0007
+#define R01_SEL_SOURCE 0x0040
+#define R01_SIM 0x0010
+#define R02_PON_PLL 0x8000
+#define R02_PON_HP 0x2000
+#define R02_PON_DAC 0x0400
+#define R02_PON_BIAS 0x0100
+#define R02_EN_AVC 0x0080
+#define R02_PON_AVC 0x0040
+#define R02_PON_LNA 0x0010
+#define R02_PON_PGAL 0x0008
+#define R02_PON_ADCL 0x0004
+#define R02_PON_PGAR 0x0002
+#define R02_PON_ADCR 0x0001
+#define R13_MTM 0x4000
+#define R14_SILENCE 0x0080
+#define R14_SDET_ON 0x0040
+#define R21_MT_ADC 0x8000
+#define R22_SEL_LNA 0x0008
+#define R22_SEL_MIC 0x0004
+#define R22_SKIP_DCFIL 0x0002
+#define R23_AGC_EN 0x0001
+
+struct uda1380_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+ int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL 1
+};
+
+#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
+#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
+#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
+
+extern struct snd_soc_dai uda1380_dai[3];
+extern struct snd_soc_codec_device soc_codec_dev_uda1380;
+
+#endif /* _UDA1380_H */
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
new file mode 100644
index 00000000000..d8ca2da8d63
--- /dev/null
+++ b/sound/soc/codecs/wm8510.c
@@ -0,0 +1,895 @@
+/*
+ * wm8510.c -- WM8510 ALSA Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8510.h"
+
+#define WM8510_VERSION "0.6"
+
+struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+/*
+ * wm8510 register cache
+ * We can't read the WM8510 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0050, 0x0000, 0x0140, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x00ff,
+ 0x0000, 0x0000, 0x0100, 0x00ff,
+ 0x0000, 0x0000, 0x012c, 0x002c,
+ 0x002c, 0x002c, 0x002c, 0x0000,
+ 0x0032, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0038, 0x000b, 0x0032, 0x0000,
+ 0x0008, 0x000c, 0x0093, 0x00e9,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0003, 0x0010, 0x0000, 0x0000,
+ 0x0000, 0x0002, 0x0001, 0x0000,
+ 0x0000, 0x0000, 0x0039, 0x0000,
+ 0x0001,
+};
+
+#define WM8510_POWER1_BIASEN 0x08
+#define WM8510_POWER1_BUFIOEN 0x10
+
+/*
+ * read wm8510 register cache
+ */
+static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == WM8510_RESET)
+ return 0;
+ if (reg >= WM8510_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write wm8510 register cache
+ */
+static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= WM8510_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8510 register space
+ */
+static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8510 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8510_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
+
+static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8510_alc[] = { "ALC", "Limiter" };
+
+static const struct soc_enum wm8510_enum[] = {
+ SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
+ SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
+ SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
+ SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
+};
+
+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8510_enum[1]),
+SOC_ENUM("ADC Companding", wm8510_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
+
+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
+SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
+};
+
+/* add non dapm controls */
+static int wm8510_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8510_snd_controls[i], codec,
+ NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_boost_controls[] = {
+SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 1),
+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_micpga_controls[] = {
+SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0),
+SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
+ &wm8510_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm8510_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
+ &wm8510_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8510_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0,
+ &wm8510_micpga_controls[0],
+ ARRAY_SIZE(wm8510_micpga_controls)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
+ &wm8510_boost_controls[0],
+ ARRAY_SIZE(wm8510_boost_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Mono output mixer */
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Microphone PGA */
+ {"Mic PGA", "MICN Switch", "MICN"},
+ {"Mic PGA", "MICP Switch", "MICP"},
+ { "Mic PGA", "AUX Switch", "Aux Input" },
+
+ /* Boost Mixer */
+ {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+ {"Boost Mixer", "Mic Volume", "MICP"},
+ {"Boost Mixer", "Aux Volume", "Aux Input"},
+
+ {"ADC", NULL, "Boost Mixer"},
+};
+
+static int wm8510_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8510_dapm_widgets,
+ ARRAY_SIZE(wm8510_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct pll_ {
+ unsigned int pre_div:4; /* prescale - 1 */
+ unsigned int n:4;
+ unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div.pre_div = 1;
+ Ndiv = target / source;
+ } else
+ pll_div.pre_div = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8510 N value %d outwith recommended range!d\n",
+ Ndiv);
+
+ pll_div.n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div.k = K;
+}
+
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ if (freq_in == 0 || freq_out == 0) {
+ /* Clock CODEC directly from MCLK */
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+ wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+
+ /* Turn off PLL */
+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+ wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+ return 0;
+ }
+
+ pll_factors(freq_out*8, freq_in);
+
+ wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+ wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+ wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+
+ /* Run CODEC from PLL instead of MCLK */
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+ wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+
+ return 0;
+}
+
+/*
+ * Configure WM8510 clock dividers.
+ */
+static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8510_OPCLKDIV:
+ reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
+ wm8510_write(codec, WM8510_GPIO, reg | div);
+ break;
+ case WM8510_MCLKDIV:
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
+ wm8510_write(codec, WM8510_CLOCK, reg | div);
+ break;
+ case WM8510_ADCCLK:
+ reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
+ wm8510_write(codec, WM8510_ADC, reg | div);
+ break;
+ case WM8510_DACCLK:
+ reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
+ wm8510_write(codec, WM8510_DAC, reg | div);
+ break;
+ case WM8510_BCLKDIV:
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
+ wm8510_write(codec, WM8510_CLOCK, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+ u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0008;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x00018;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8510_write(codec, WM8510_IFACE, iface);
+ wm8510_write(codec, WM8510_CLOCK, clk);
+ return 0;
+}
+
+static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
+ u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0020;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0040;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x0060;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case SNDRV_PCM_RATE_8000:
+ adn |= 0x5 << 1;
+ break;
+ case SNDRV_PCM_RATE_11025:
+ adn |= 0x4 << 1;
+ break;
+ case SNDRV_PCM_RATE_16000:
+ adn |= 0x3 << 1;
+ break;
+ case SNDRV_PCM_RATE_22050:
+ adn |= 0x2 << 1;
+ break;
+ case SNDRV_PCM_RATE_32000:
+ adn |= 0x1 << 1;
+ break;
+ case SNDRV_PCM_RATE_44100:
+ case SNDRV_PCM_RATE_48000:
+ break;
+ }
+
+ wm8510_write(codec, WM8510_IFACE, iface);
+ wm8510_write(codec, WM8510_ADD, adn);
+ return 0;
+}
+
+static int wm8510_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+
+ if (mute)
+ wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+ else
+ wm8510_write(codec, WM8510_DAC, mute_reg);
+ return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8510_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ power1 |= 0x1; /* VMID 50k */
+ wm8510_write(codec, WM8510_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
+
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Initial cap charge at VMID 5k */
+ wm8510_write(codec, WM8510_POWER1, power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ wm8510_write(codec, WM8510_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ wm8510_write(codec, WM8510_POWER1, 0);
+ wm8510_write(codec, WM8510_POWER2, 0);
+ wm8510_write(codec, WM8510_POWER3, 0);
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8510_dai = {
+ .name = "WM8510 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8510_RATES,
+ .formats = WM8510_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8510_RATES,
+ .formats = WM8510_FORMATS,},
+ .ops = {
+ .hw_params = wm8510_pcm_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = wm8510_mute,
+ .set_fmt = wm8510_set_dai_fmt,
+ .set_clkdiv = wm8510_set_dai_clkdiv,
+ .set_pll = wm8510_set_dai_pll,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8510_dai);
+
+static int wm8510_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;
+
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8510_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8510_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the WM8510 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8510_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "WM8510";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8510_read_reg_cache;
+ codec->write = wm8510_write;
+ codec->set_bias_level = wm8510_set_bias_level;
+ codec->dai = &wm8510_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
+ codec->reg_cache = kmemdup(wm8510_reg, sizeof(wm8510_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8510_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8510: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8510_add_controls(codec);
+ wm8510_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8510: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *wm8510_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8510 2 wire address is 0x1a
+ */
+
+static int wm8510_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = wm8510_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = wm8510_init(socdev);
+ if (ret < 0)
+ pr_err("failed to initialise WM8510\n");
+
+ return ret;
+}
+
+static int wm8510_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id wm8510_i2c_id[] = {
+ { "wm8510", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
+
+static struct i2c_driver wm8510_i2c_driver = {
+ .driver = {
+ .name = "WM8510 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8510_i2c_probe,
+ .remove = wm8510_i2c_remove,
+ .id_table = wm8510_i2c_id,
+};
+
+static int wm8510_add_i2c_device(struct platform_device *pdev,
+ const struct wm8510_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8510_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8510", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8510_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8510_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8510_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8510_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8510\n");
+
+ return ret;
+}
+
+static int __devexit wm8510_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8510_spi_driver = {
+ .driver = {
+ .name = "wm8510",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8510_spi_probe,
+ .remove = __devexit_p(wm8510_spi_remove),
+};
+
+static int wm8510_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
+static int wm8510_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8510_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ pr_info("WM8510 Audio Codec %s", WM8510_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ wm8510_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = wm8510_add_i2c_device(pdev, setup);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8510_spi_write;
+ ret = spi_register_driver(&wm8510_spi_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add spi driver");
+ }
+#endif
+
+ if (ret != 0)
+ kfree(codec);
+ return ret;
+}
+
+/* power down chip */
+static int wm8510_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&wm8510_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8510_spi_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8510 = {
+ .probe = wm8510_probe,
+ .remove = wm8510_remove,
+ .suspend = wm8510_suspend,
+ .resume = wm8510_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
+
+MODULE_DESCRIPTION("ASoC WM8510 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
new file mode 100644
index 00000000000..bdefcf5c69f
--- /dev/null
+++ b/sound/soc/codecs/wm8510.h
@@ -0,0 +1,105 @@
+/*
+ * wm8510.h -- WM8510 Soc Audio driver
+ *
+ * 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 _WM8510_H
+#define _WM8510_H
+
+/* WM8510 register space */
+
+#define WM8510_RESET 0x0
+#define WM8510_POWER1 0x1
+#define WM8510_POWER2 0x2
+#define WM8510_POWER3 0x3
+#define WM8510_IFACE 0x4
+#define WM8510_COMP 0x5
+#define WM8510_CLOCK 0x6
+#define WM8510_ADD 0x7
+#define WM8510_GPIO 0x8
+#define WM8510_DAC 0xa
+#define WM8510_DACVOL 0xb
+#define WM8510_ADC 0xe
+#define WM8510_ADCVOL 0xf
+#define WM8510_EQ1 0x12
+#define WM8510_EQ2 0x13
+#define WM8510_EQ3 0x14
+#define WM8510_EQ4 0x15
+#define WM8510_EQ5 0x16
+#define WM8510_DACLIM1 0x18
+#define WM8510_DACLIM2 0x19
+#define WM8510_NOTCH1 0x1b
+#define WM8510_NOTCH2 0x1c
+#define WM8510_NOTCH3 0x1d
+#define WM8510_NOTCH4 0x1e
+#define WM8510_ALC1 0x20
+#define WM8510_ALC2 0x21
+#define WM8510_ALC3 0x22
+#define WM8510_NGATE 0x23
+#define WM8510_PLLN 0x24
+#define WM8510_PLLK1 0x25
+#define WM8510_PLLK2 0x26
+#define WM8510_PLLK3 0x27
+#define WM8510_ATTEN 0x28
+#define WM8510_INPUT 0x2c
+#define WM8510_INPPGA 0x2d
+#define WM8510_ADCBOOST 0x2f
+#define WM8510_OUTPUT 0x31
+#define WM8510_SPKMIX 0x32
+#define WM8510_SPKVOL 0x36
+#define WM8510_MONOMIX 0x38
+
+#define WM8510_CACHEREGNUM 57
+
+/* Clock divider Id's */
+#define WM8510_OPCLKDIV 0
+#define WM8510_MCLKDIV 1
+#define WM8510_ADCCLK 2
+#define WM8510_DACCLK 3
+#define WM8510_BCLKDIV 4
+
+/* DAC clock dividers */
+#define WM8510_DACCLK_F2 (1 << 3)
+#define WM8510_DACCLK_F4 (0 << 3)
+
+/* ADC clock dividers */
+#define WM8510_ADCCLK_F2 (1 << 3)
+#define WM8510_ADCCLK_F4 (0 << 3)
+
+/* PLL Out dividers */
+#define WM8510_OPCLKDIV_1 (0 << 4)
+#define WM8510_OPCLKDIV_2 (1 << 4)
+#define WM8510_OPCLKDIV_3 (2 << 4)
+#define WM8510_OPCLKDIV_4 (3 << 4)
+
+/* BCLK clock dividers */
+#define WM8510_BCLKDIV_1 (0 << 2)
+#define WM8510_BCLKDIV_2 (1 << 2)
+#define WM8510_BCLKDIV_4 (2 << 2)
+#define WM8510_BCLKDIV_8 (3 << 2)
+#define WM8510_BCLKDIV_16 (4 << 2)
+#define WM8510_BCLKDIV_32 (5 << 2)
+
+/* MCLK clock dividers */
+#define WM8510_MCLKDIV_1 (0 << 5)
+#define WM8510_MCLKDIV_1_5 (1 << 5)
+#define WM8510_MCLKDIV_2 (2 << 5)
+#define WM8510_MCLKDIV_3 (3 << 5)
+#define WM8510_MCLKDIV_4 (4 << 5)
+#define WM8510_MCLKDIV_6 (5 << 5)
+#define WM8510_MCLKDIV_8 (6 << 5)
+#define WM8510_MCLKDIV_12 (7 << 5)
+
+struct wm8510_setup_data {
+ int spi;
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8510_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
new file mode 100644
index 00000000000..627ebfb4209
--- /dev/null
+++ b/sound/soc/codecs/wm8580.c
@@ -0,0 +1,1053 @@
+/*
+ * wm8580.c -- WM8580 ALSA Soc Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Notes:
+ * The WM8580 is a multichannel codec with S/PDIF support, featuring six
+ * DAC channels and two ADC channels.
+ *
+ * Currently only the primary audio interface is supported - S/PDIF and
+ * the secondary audio interfaces are not.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.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/tlv.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+
+#include "wm8580.h"
+
+#define WM8580_VERSION "0.1"
+
+struct pll_state {
+ unsigned int in;
+ unsigned int out;
+};
+
+/* codec private data */
+struct wm8580_priv {
+ struct pll_state a;
+ struct pll_state b;
+};
+
+/* WM8580 register space */
+#define WM8580_PLLA1 0x00
+#define WM8580_PLLA2 0x01
+#define WM8580_PLLA3 0x02
+#define WM8580_PLLA4 0x03
+#define WM8580_PLLB1 0x04
+#define WM8580_PLLB2 0x05
+#define WM8580_PLLB3 0x06
+#define WM8580_PLLB4 0x07
+#define WM8580_CLKSEL 0x08
+#define WM8580_PAIF1 0x09
+#define WM8580_PAIF2 0x0A
+#define WM8580_SAIF1 0x0B
+#define WM8580_PAIF3 0x0C
+#define WM8580_PAIF4 0x0D
+#define WM8580_SAIF2 0x0E
+#define WM8580_DAC_CONTROL1 0x0F
+#define WM8580_DAC_CONTROL2 0x10
+#define WM8580_DAC_CONTROL3 0x11
+#define WM8580_DAC_CONTROL4 0x12
+#define WM8580_DAC_CONTROL5 0x13
+#define WM8580_DIGITAL_ATTENUATION_DACL1 0x14
+#define WM8580_DIGITAL_ATTENUATION_DACR1 0x15
+#define WM8580_DIGITAL_ATTENUATION_DACL2 0x16
+#define WM8580_DIGITAL_ATTENUATION_DACR2 0x17
+#define WM8580_DIGITAL_ATTENUATION_DACL3 0x18
+#define WM8580_DIGITAL_ATTENUATION_DACR3 0x19
+#define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C
+#define WM8580_ADC_CONTROL1 0x1D
+#define WM8580_SPDTXCHAN0 0x1E
+#define WM8580_SPDTXCHAN1 0x1F
+#define WM8580_SPDTXCHAN2 0x20
+#define WM8580_SPDTXCHAN3 0x21
+#define WM8580_SPDTXCHAN4 0x22
+#define WM8580_SPDTXCHAN5 0x23
+#define WM8580_SPDMODE 0x24
+#define WM8580_INTMASK 0x25
+#define WM8580_GPO1 0x26
+#define WM8580_GPO2 0x27
+#define WM8580_GPO3 0x28
+#define WM8580_GPO4 0x29
+#define WM8580_GPO5 0x2A
+#define WM8580_INTSTAT 0x2B
+#define WM8580_SPDRXCHAN1 0x2C
+#define WM8580_SPDRXCHAN2 0x2D
+#define WM8580_SPDRXCHAN3 0x2E
+#define WM8580_SPDRXCHAN4 0x2F
+#define WM8580_SPDRXCHAN5 0x30
+#define WM8580_SPDSTAT 0x31
+#define WM8580_PWRDN1 0x32
+#define WM8580_PWRDN2 0x33
+#define WM8580_READBACK 0x34
+#define WM8580_RESET 0x35
+
+/* PLLB4 (register 7h) */
+#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60
+#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20
+#define WM8580_PLLB4_MCLKOUTSRC_PLLB 0x40
+#define WM8580_PLLB4_MCLKOUTSRC_OSC 0x60
+
+#define WM8580_PLLB4_CLKOUTSRC_MASK 0x180
+#define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080
+#define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100
+#define WM8580_PLLB4_CLKOUTSRC_OSCCLK 0x180
+
+/* CLKSEL (register 8h) */
+#define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03
+#define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01
+#define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02
+
+/* AIF control 1 (registers 9h-bh) */
+#define WM8580_AIF_RATE_MASK 0x7
+#define WM8580_AIF_RATE_128 0x0
+#define WM8580_AIF_RATE_192 0x1
+#define WM8580_AIF_RATE_256 0x2
+#define WM8580_AIF_RATE_384 0x3
+#define WM8580_AIF_RATE_512 0x4
+#define WM8580_AIF_RATE_768 0x5
+#define WM8580_AIF_RATE_1152 0x6
+
+#define WM8580_AIF_BCLKSEL_MASK 0x18
+#define WM8580_AIF_BCLKSEL_64 0x00
+#define WM8580_AIF_BCLKSEL_128 0x08
+#define WM8580_AIF_BCLKSEL_256 0x10
+#define WM8580_AIF_BCLKSEL_SYSCLK 0x18
+
+#define WM8580_AIF_MS 0x20
+
+#define WM8580_AIF_CLKSRC_MASK 0xc0
+#define WM8580_AIF_CLKSRC_PLLA 0x40
+#define WM8580_AIF_CLKSRC_PLLB 0x40
+#define WM8580_AIF_CLKSRC_MCLK 0xc0
+
+/* AIF control 2 (registers ch-eh) */
+#define WM8580_AIF_FMT_MASK 0x03
+#define WM8580_AIF_FMT_RIGHTJ 0x00
+#define WM8580_AIF_FMT_LEFTJ 0x01
+#define WM8580_AIF_FMT_I2S 0x02
+#define WM8580_AIF_FMT_DSP 0x03
+
+#define WM8580_AIF_LENGTH_MASK 0x0c
+#define WM8580_AIF_LENGTH_16 0x00
+#define WM8580_AIF_LENGTH_20 0x04
+#define WM8580_AIF_LENGTH_24 0x08
+#define WM8580_AIF_LENGTH_32 0x0c
+
+#define WM8580_AIF_LRP 0x10
+#define WM8580_AIF_BCP 0x20
+
+/* Powerdown Register 1 (register 32h) */
+#define WM8580_PWRDN1_PWDN 0x001
+#define WM8580_PWRDN1_ALLDACPD 0x040
+
+/* Powerdown Register 2 (register 33h) */
+#define WM8580_PWRDN2_OSSCPD 0x001
+#define WM8580_PWRDN2_PLLAPD 0x002
+#define WM8580_PWRDN2_PLLBPD 0x004
+#define WM8580_PWRDN2_SPDIFPD 0x008
+#define WM8580_PWRDN2_SPDIFTXD 0x010
+#define WM8580_PWRDN2_SPDIFRXD 0x020
+
+#define WM8580_DAC_CONTROL5_MUTEALL 0x10
+
+/*
+ * wm8580 register cache
+ * We can't read the WM8580 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8580_reg[] = {
+ 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/
+ 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/
+ 0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/
+ 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/
+ 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/
+ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/
+ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/
+ 0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/
+ 0x0000, 0x0000, 0x0031, 0x000b, /*R35*/
+ 0x0039, 0x0000, 0x0010, 0x0032, /*R39*/
+ 0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/
+ 0x0000, 0x0000, 0x0000, 0x0000, /*R47*/
+ 0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/
+ 0x0000, 0x0000 /*R53*/
+};
+
+/*
+ * read wm8580 register cache
+ */
+static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
+ return cache[reg];
+}
+
+/*
+ * write wm8580 register cache
+ */
+static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8580 register space
+ */
+static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
+
+ /* Registers are 9 bits wide */
+ value &= 0x1ff;
+
+ switch (reg) {
+ case WM8580_RESET:
+ /* Uncached */
+ break;
+ default:
+ if (value == wm8580_read_reg_cache(codec, reg))
+ return 0;
+ }
+
+ /* data is
+ * D15..D9 WM8580 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8580_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static inline unsigned int wm8580_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ default:
+ return wm8580_read_reg_cache(codec, reg);
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+
+static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 24) & 0xff;
+ int ret;
+ u16 val;
+
+ /* Clear the register cache so we write without VU set */
+ wm8580_write_reg_cache(codec, reg, 0);
+ wm8580_write_reg_cache(codec, reg2, 0);
+
+ ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* Now write again with the volume update bit set */
+ val = wm8580_read_reg_cache(codec, reg);
+ wm8580_write(codec, reg, val | 0x0100);
+
+ val = wm8580_read_reg_cache(codec, reg2);
+ wm8580_write(codec, reg2, val | 0x0100);
+
+ return 0;
+}
+
+#define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_2r, \
+ .get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \
+ .private_value = (reg_left) | ((shift) << 8) | \
+ ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+
+static const struct snd_kcontrol_new wm8580_snd_controls[] = {
+SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume",
+ WM8580_DIGITAL_ATTENUATION_DACL1,
+ WM8580_DIGITAL_ATTENUATION_DACR1,
+ 0, 0xff, 0, dac_tlv),
+SOC_WM8580_OUT_DOUBLE_R_TLV("DAC2 Playback Volume",
+ WM8580_DIGITAL_ATTENUATION_DACL2,
+ WM8580_DIGITAL_ATTENUATION_DACR2,
+ 0, 0xff, 0, dac_tlv),
+SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume",
+ WM8580_DIGITAL_ATTENUATION_DACL3,
+ WM8580_DIGITAL_ATTENUATION_DACR3,
+ 0, 0xff, 0, dac_tlv),
+
+SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0),
+SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0),
+SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0),
+
+SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4, 0, 1, 1, 0),
+SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0),
+SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0),
+
+SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0),
+SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 0),
+SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 0),
+SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 0),
+
+SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0),
+SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
+};
+
+/* Add non-DAPM controls */
+static int wm8580_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8580_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
+SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
+SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1),
+
+SND_SOC_DAPM_OUTPUT("VOUT1L"),
+SND_SOC_DAPM_OUTPUT("VOUT1R"),
+SND_SOC_DAPM_OUTPUT("VOUT2L"),
+SND_SOC_DAPM_OUTPUT("VOUT2R"),
+SND_SOC_DAPM_OUTPUT("VOUT3L"),
+SND_SOC_DAPM_OUTPUT("VOUT3R"),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1),
+
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ { "VOUT1L", NULL, "DAC1" },
+ { "VOUT1R", NULL, "DAC1" },
+
+ { "VOUT2L", NULL, "DAC2" },
+ { "VOUT2R", NULL, "DAC2" },
+
+ { "VOUT3L", NULL, "DAC3" },
+ { "VOUT3R", NULL, "DAC3" },
+
+ { "ADC", NULL, "AINL" },
+ { "ADC", NULL, "AINR" },
+};
+
+static int wm8580_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets,
+ ARRAY_SIZE(wm8580_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+ u32 prescale:1;
+ u32 postscale:1;
+ u32 freqmode:2;
+ u32 n:4;
+ u32 k:24;
+};
+
+/* The size in bits of the pll divide */
+#define FIXED_PLL_SIZE (1 << 22)
+
+/* PLL rate to output rate divisions */
+static struct {
+ unsigned int div;
+ unsigned int freqmode;
+ unsigned int postscale;
+} post_table[] = {
+ { 2, 0, 0 },
+ { 4, 0, 1 },
+ { 4, 1, 0 },
+ { 8, 1, 1 },
+ { 8, 2, 0 },
+ { 16, 2, 1 },
+ { 12, 3, 0 },
+ { 24, 3, 1 }
+};
+
+static int pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+ int i;
+
+ pr_debug("wm8580: PLL %dHz->%dHz\n", source, target);
+
+ /* Scale the output frequency up; the PLL should run in the
+ * region of 90-100MHz.
+ */
+ for (i = 0; i < ARRAY_SIZE(post_table); i++) {
+ if (target * post_table[i].div >= 90000000 &&
+ target * post_table[i].div <= 100000000) {
+ pll_div->freqmode = post_table[i].freqmode;
+ pll_div->postscale = post_table[i].postscale;
+ target *= post_table[i].div;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(post_table)) {
+ printk(KERN_ERR "wm8580: Unable to scale output frequency "
+ "%u\n", target);
+ return -EINVAL;
+ }
+
+ Ndiv = target / source;
+
+ if (Ndiv < 5) {
+ source /= 2;
+ pll_div->prescale = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->prescale = 0;
+
+ if ((Ndiv < 5) || (Ndiv > 13)) {
+ printk(KERN_ERR
+ "WM8580 N=%d outside supported range\n", Ndiv);
+ return -EINVAL;
+ }
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ pll_div->k = K;
+
+ pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n",
+ pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode,
+ pll_div->postscale);
+
+ return 0;
+}
+
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ int offset;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8580_priv *wm8580 = codec->private_data;
+ struct pll_state *state;
+ struct _pll_div pll_div;
+ unsigned int reg;
+ unsigned int pwr_mask;
+ int ret;
+
+ /* GCC isn't able to work out the ifs below for initialising/using
+ * pll_div so suppress warnings.
+ */
+ memset(&pll_div, 0, sizeof(pll_div));
+
+ switch (pll_id) {
+ case WM8580_PLLA:
+ state = &wm8580->a;
+ offset = 0;
+ pwr_mask = WM8580_PWRDN2_PLLAPD;
+ break;
+ case WM8580_PLLB:
+ state = &wm8580->b;
+ offset = 4;
+ pwr_mask = WM8580_PWRDN2_PLLBPD;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (freq_in && freq_out) {
+ ret = pll_factors(&pll_div, freq_out, freq_in);
+ if (ret != 0)
+ return ret;
+ }
+
+ state->in = freq_in;
+ state->out = freq_out;
+
+ /* Always disable the PLL - it is not safe to leave it running
+ * while reprogramming it.
+ */
+ reg = wm8580_read(codec, WM8580_PWRDN2);
+ wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask);
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+ wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
+ wm8580_write(codec, WM8580_PLLA3 + offset,
+ (pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
+
+ reg = wm8580_read(codec, WM8580_PLLA4 + offset);
+ reg &= ~0x3f;
+ reg |= pll_div.prescale | pll_div.postscale << 1 |
+ pll_div.freqmode << 4;
+
+ wm8580_write(codec, WM8580_PLLA4 + offset, reg);
+
+ /* All done, turn it on */
+ reg = wm8580_read(codec, WM8580_PWRDN2);
+ wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai = rtd->dai;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id);
+
+ paifb &= ~WM8580_AIF_LENGTH_MASK;
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ paifb |= WM8580_AIF_LENGTH_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ paifb |= WM8580_AIF_LENGTH_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ paifb |= WM8580_AIF_LENGTH_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
+ return 0;
+}
+
+static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int aifa;
+ unsigned int aifb;
+ int can_invert_lrclk;
+
+ aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id);
+ aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id);
+
+ aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aifa &= ~WM8580_AIF_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aifa |= WM8580_AIF_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ can_invert_lrclk = 1;
+ aifb |= WM8580_AIF_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ can_invert_lrclk = 1;
+ aifb |= WM8580_AIF_FMT_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ can_invert_lrclk = 1;
+ aifb |= WM8580_AIF_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ can_invert_lrclk = 0;
+ aifb |= WM8580_AIF_FMT_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ can_invert_lrclk = 0;
+ aifb |= WM8580_AIF_FMT_DSP;
+ aifb |= WM8580_AIF_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ if (!can_invert_lrclk)
+ return -EINVAL;
+ aifb |= WM8580_AIF_BCP;
+ aifb |= WM8580_AIF_LRP;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ aifb |= WM8580_AIF_BCP;
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ if (!can_invert_lrclk)
+ return -EINVAL;
+ aifb |= WM8580_AIF_LRP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
+ wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
+
+ return 0;
+}
+
+static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ switch (div_id) {
+ case WM8580_MCLK:
+ reg = wm8580_read(codec, WM8580_PLLB4);
+ reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
+
+ switch (div) {
+ case WM8580_CLKSRC_MCLK:
+ /* Input */
+ break;
+
+ case WM8580_CLKSRC_PLLA:
+ reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA;
+ break;
+ case WM8580_CLKSRC_PLLB:
+ reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB;
+ break;
+
+ case WM8580_CLKSRC_OSC:
+ reg |= WM8580_PLLB4_MCLKOUTSRC_OSC;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ wm8580_write(codec, WM8580_PLLB4, reg);
+ break;
+
+ case WM8580_DAC_CLKSEL:
+ reg = wm8580_read(codec, WM8580_CLKSEL);
+ reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK;
+
+ switch (div) {
+ case WM8580_CLKSRC_MCLK:
+ break;
+
+ case WM8580_CLKSRC_PLLA:
+ reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA;
+ break;
+
+ case WM8580_CLKSRC_PLLB:
+ reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ wm8580_write(codec, WM8580_CLKSEL, reg);
+ break;
+
+ case WM8580_CLKOUTSRC:
+ reg = wm8580_read(codec, WM8580_PLLB4);
+ reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
+
+ switch (div) {
+ case WM8580_CLKSRC_NONE:
+ break;
+
+ case WM8580_CLKSRC_PLLA:
+ reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK;
+ break;
+
+ case WM8580_CLKSRC_PLLB:
+ reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK;
+ break;
+
+ case WM8580_CLKSRC_OSC:
+ reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ wm8580_write(codec, WM8580_PLLB4, reg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ reg = wm8580_read(codec, WM8580_DAC_CONTROL5);
+
+ if (mute)
+ reg |= WM8580_DAC_CONTROL5_MUTEALL;
+ else
+ reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
+
+ wm8580_write(codec, WM8580_DAC_CONTROL5, reg);
+
+ return 0;
+}
+
+static int wm8580_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ reg = wm8580_read(codec, WM8580_PWRDN1);
+ wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8580_dai[] = {
+ {
+ .name = "WM8580 PAIFRX",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = WM8580_FORMATS,
+ },
+ .ops = {
+ .hw_params = wm8580_paif_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = wm8580_set_paif_dai_fmt,
+ .set_clkdiv = wm8580_set_dai_clkdiv,
+ .set_pll = wm8580_set_dai_pll,
+ .digital_mute = wm8580_digital_mute,
+ },
+ },
+ {
+ .name = "WM8580 PAIFTX",
+ .id = 1,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = WM8580_FORMATS,
+ },
+ .ops = {
+ .hw_params = wm8580_paif_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = wm8580_set_paif_dai_fmt,
+ .set_clkdiv = wm8580_set_dai_clkdiv,
+ .set_pll = wm8580_set_dai_pll,
+ },
+ },
+};
+EXPORT_SYMBOL_GPL(wm8580_dai);
+
+/*
+ * initialise the WM8580 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8580_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "WM8580";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8580_read_reg_cache;
+ codec->write = wm8580_write;
+ codec->set_bias_level = wm8580_set_bias_level;
+ codec->dai = wm8580_dai;
+ codec->num_dai = ARRAY_SIZE(wm8580_dai);
+ codec->reg_cache_size = ARRAY_SIZE(wm8580_reg);
+ codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg),
+ GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* Get the codec into a known state */
+ wm8580_write(codec, WM8580_RESET, 0);
+
+ /* Power up and get individual control of the DACs */
+ wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) &
+ ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD));
+
+ /* Make VMID high impedence */
+ wm8580_write(codec, WM8580_ADC_CONTROL1,
+ wm8580_read(codec, WM8580_ADC_CONTROL1) & ~0x100);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8580: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ wm8580_add_controls(codec);
+ wm8580_add_widgets(codec);
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8580: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+static struct snd_soc_device *wm8580_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8580 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8580_i2c_driver;
+static struct i2c_client client_template;
+
+static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = wm8580_socdev;
+ struct wm8580_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) {
+ dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = wm8580_init(socdev);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to initialise WM8580\n");
+ goto err;
+ }
+
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int wm8580_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 wm8580_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, wm8580_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8580_i2c_driver = {
+ .driver = {
+ .name = "WM8580 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = wm8580_i2c_attach,
+ .detach_client = wm8580_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "WM8580",
+ .driver = &wm8580_i2c_driver,
+};
+#endif
+
+static int wm8580_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8580_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct wm8580_priv *wm8580;
+ int ret = 0;
+
+ pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
+ if (wm8580 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = wm8580;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ wm8580_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(&wm8580_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int wm8580_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8580_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8580 = {
+ .probe = wm8580_probe,
+ .remove = wm8580_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
+
+MODULE_DESCRIPTION("ASoC WM8580 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h
new file mode 100644
index 00000000000..589ddaba21d
--- /dev/null
+++ b/sound/soc/codecs/wm8580.h
@@ -0,0 +1,42 @@
+/*
+ * wm8580.h -- audio driver for WM8580
+ *
+ * Copyright 2008 Samsung Electronics.
+ * Author: Ryu Euiyoul
+ * ryu.real@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _WM8580_H
+#define _WM8580_H
+
+#define WM8580_PLLA 1
+#define WM8580_PLLB 2
+
+#define WM8580_MCLK 1
+#define WM8580_DAC_CLKSEL 2
+#define WM8580_CLKOUTSRC 3
+
+#define WM8580_CLKSRC_MCLK 1
+#define WM8580_CLKSRC_PLLA 2
+#define WM8580_CLKSRC_PLLB 3
+#define WM8580_CLKSRC_OSC 4
+#define WM8580_CLKSRC_NONE 5
+
+struct wm8580_setup_data {
+ unsigned short i2c_address;
+};
+
+#define WM8580_DAI_PAIFRX 0
+#define WM8580_DAI_PAIFTX 1
+
+extern struct snd_soc_dai wm8580_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_wm8580;
+
+#endif
+
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 0cf9265fca8..7f8a7e36b33 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -28,28 +29,8 @@
#include "wm8731.h"
-#define AUDIO_NAME "wm8731"
#define WM8731_VERSION "0.13"
-/*
- * Debug
- */
-
-#define WM8731_DEBUG 0
-
-#ifdef WM8731_DEBUG
-#define dbg(format, arg...) \
- printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
- printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
- printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
- printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
struct snd_soc_codec_device soc_codec_dev_wm8731;
/* codec private data */
@@ -193,7 +174,7 @@ SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"),
};
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -214,22 +195,14 @@ static const char *intercon[][3] = {
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
static int wm8731_add_widgets(struct snd_soc_codec *codec)
{
- int i;
+ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+ ARRAY_SIZE(wm8731_dapm_widgets));
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
-
- /* set up audio path interconnects */
- for (i = 0; intercon[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, intercon[i][0],
- intercon[i][1], intercon[i][2]);
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -345,7 +318,7 @@ static void wm8731_shutdown(struct snd_pcm_substream *substream)
}
}
-static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8731_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
@@ -357,7 +330,7 @@ static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -376,7 +349,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
}
-static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -435,29 +408,29 @@ static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8731_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
wm8731_write(codec, WM8731_PWR, reg);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
wm8731_write(codec, WM8731_PWR, reg | 0x0040);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
wm8731_write(codec, WM8731_ACTIVE, 0x0);
wm8731_write(codec, WM8731_PWR, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -470,7 +443,7 @@ static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
-struct snd_soc_codec_dai wm8731_dai = {
+struct snd_soc_dai wm8731_dai = {
.name = "WM8731",
.playback = {
.stream_name = "Playback",
@@ -503,7 +476,7 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_codec *codec = socdev->codec;
wm8731_write(codec, WM8731_ACTIVE, 0x0);
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -521,8 +494,8 @@ static int wm8731_resume(struct platform_device *pdev)
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
}
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
- wm8731_dapm_event(codec, codec->suspend_dapm_state);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8731_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -539,10 +512,10 @@ static int wm8731_init(struct snd_soc_device *socdev)
codec->owner = THIS_MODULE;
codec->read = wm8731_read_reg_cache;
codec->write = wm8731_write;
- codec->dapm_event = wm8731_dapm_event;
+ codec->set_bias_level = wm8731_set_bias_level;
codec->dai = &wm8731_dai;
codec->num_dai = 1;
- codec->reg_cache_size = sizeof(wm8731_reg);
+ codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
@@ -557,7 +530,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
}
/* power on device */
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* set the update bits */
reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
@@ -597,90 +570,144 @@ static struct snd_soc_device *wm8731_socdev;
* low = 0x1a
* high = 0x1b
*/
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-static struct i2c_driver wm8731_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
- around */
-
-static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8731_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8731_socdev;
- struct wm8731_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->codec;
- struct i2c_client *i2c;
int ret;
- if (addr != setup->i2c_address)
- return -ENODEV;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
- if (i2c == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = i2c_attach_client(i2c);
- if (ret < 0) {
- err("failed to attach codec at addr %x\n", addr);
- goto err;
- }
-
ret = wm8731_init(socdev);
- if (ret < 0) {
- err("failed to initialise WM8731\n");
- goto err;
- }
- return ret;
+ if (ret < 0)
+ pr_err("failed to initialise WM8731\n");
-err:
- kfree(codec);
- kfree(i2c);
return ret;
}
-static int wm8731_i2c_detach(struct i2c_client *client)
+static int wm8731_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
- i2c_detach_client(client);
kfree(codec->reg_cache);
- kfree(client);
return 0;
}
-static int wm8731_i2c_attach(struct i2c_adapter *adap)
-{
- return i2c_probe(adap, &addr_data, wm8731_codec_probe);
-}
+static const struct i2c_device_id wm8731_i2c_id[] = {
+ { "wm8731", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
-/* corgi i2c codec control layer */
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
.name = "WM8731 I2C Codec",
.owner = THIS_MODULE,
},
- .id = I2C_DRIVERID_WM8731,
- .attach_adapter = wm8731_i2c_attach,
- .detach_client = wm8731_i2c_detach,
- .command = NULL,
+ .probe = wm8731_i2c_probe,
+ .remove = wm8731_i2c_remove,
+ .id_table = wm8731_i2c_id,
};
-static struct i2c_client client_template = {
- .name = "WM8731",
- .driver = &wm8731_i2c_driver,
-};
+static int wm8731_add_i2c_device(struct platform_device *pdev,
+ const struct wm8731_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8731_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8731_i2c_driver);
+ return -ENODEV;
+}
#endif
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8731_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8731_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8731_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8731\n");
+
+ return ret;
+}
+
+static int __devexit wm8731_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8731_spi_driver = {
+ .driver = {
+ .name = "wm8731",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8731_spi_probe,
+ .remove = __devexit_p(wm8731_spi_remove),
+};
+
+static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
static int wm8731_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -689,7 +716,7 @@ static int wm8731_probe(struct platform_device *pdev)
struct wm8731_priv *wm8731;
int ret = 0;
- info("WM8731 Audio Codec %s", WM8731_VERSION);
+ pr_info("WM8731 Audio Codec %s", WM8731_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -709,17 +736,27 @@ static int wm8731_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&codec->dapm_paths);
wm8731_socdev = socdev;
+ ret = -ENODEV;
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t)i2c_master_send;
- ret = i2c_add_driver(&wm8731_i2c_driver);
+ ret = wm8731_add_i2c_device(pdev, setup);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8731_spi_write;
+ ret = spi_register_driver(&wm8731_spi_driver);
if (ret != 0)
- printk(KERN_ERR "can't add i2c driver");
+ printk(KERN_ERR "can't add spi driver");
}
-#else
- /* Add other interfaces here */
#endif
+
+ if (ret != 0) {
+ kfree(codec->private_data);
+ kfree(codec);
+ }
return ret;
}
@@ -730,13 +767,17 @@ static int wm8731_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8731_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8731_spi_driver);
+#endif
kfree(codec->private_data);
kfree(codec);
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 5bcab6a7afb..95190e9c0c1 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -35,10 +35,12 @@
#define WM8731_DAI 0
struct wm8731_setup_data {
+ int spi;
+ int i2c_bus;
unsigned short i2c_address;
};
-extern struct snd_soc_codec_dai wm8731_dai;
+extern struct snd_soc_dai wm8731_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8731;
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 16cd5d4d5ad..9b7296ee5b0 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -28,28 +29,8 @@
#include "wm8750.h"
-#define AUDIO_NAME "WM8750"
#define WM8750_VERSION "0.12"
-/*
- * Debug
- */
-
-#define WM8750_DEBUG 0
-
-#ifdef WM8750_DEBUG
-#define dbg(format, arg...) \
- printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
- printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
- printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
- printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
/* codec private data */
struct wm8750_priv {
unsigned int sysclk;
@@ -367,8 +348,9 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("ROUT1"),
SND_SOC_DAPM_OUTPUT("LOUT2"),
SND_SOC_DAPM_OUTPUT("ROUT2"),
- SND_SOC_DAPM_OUTPUT("MONO"),
+ SND_SOC_DAPM_OUTPUT("MONO1"),
SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("VREF"),
SND_SOC_DAPM_INPUT("LINPUT1"),
SND_SOC_DAPM_INPUT("LINPUT2"),
@@ -378,7 +360,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("RINPUT3"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* left mixer */
{"Left Mixer", "Playback Switch", "Left DAC"},
{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
@@ -470,22 +452,14 @@ static const char *audio_map[][3] = {
/* ADC */
{"Left ADC", NULL, "Left ADC Mux"},
{"Right ADC", NULL, "Right ADC Mux"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
static int wm8750_add_widgets(struct snd_soc_codec *codec)
{
- int i;
+ snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+ ARRAY_SIZE(wm8750_dapm_widgets));
- for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
-
- /* set up audio path audio_mapnects */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -563,7 +537,7 @@ static inline int get_coeff(int mclk, int rate)
return -EINVAL;
}
-static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -581,7 +555,7 @@ static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
return -EINVAL;
}
-static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -674,7 +648,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8750_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
@@ -686,29 +660,29 @@ static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8750_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
wm8750_write(codec, WM8750_PWR1, 0x0001);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -719,7 +693,7 @@ static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
-struct snd_soc_codec_dai wm8750_dai = {
+struct snd_soc_dai wm8750_dai = {
.name = "WM8750",
.playback = {
.stream_name = "Playback",
@@ -748,7 +722,7 @@ static void wm8750_work(struct work_struct *work)
{
struct snd_soc_codec *codec =
container_of(work, struct snd_soc_codec, delayed_work.work);
- wm8750_dapm_event(codec, codec->dapm_state);
+ wm8750_set_bias_level(codec, codec->bias_level);
}
static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
@@ -756,7 +730,7 @@ static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -777,12 +751,12 @@ static int wm8750_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* charge wm8750 caps */
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D0;
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_ON;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(1000));
}
@@ -803,10 +777,10 @@ static int wm8750_init(struct snd_soc_device *socdev)
codec->owner = THIS_MODULE;
codec->read = wm8750_read_reg_cache;
codec->write = wm8750_write;
- codec->dapm_event = wm8750_dapm_event;
+ codec->set_bias_level = wm8750_set_bias_level;
codec->dai = &wm8750_dai;
codec->num_dai = 1;
- codec->reg_cache_size = sizeof(wm8750_reg);
+ codec->reg_cache_size = ARRAY_SIZE(wm8750_reg);
codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
@@ -821,8 +795,8 @@ static int wm8750_init(struct snd_soc_device *socdev)
}
/* charge output caps */
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
/* set the update bits */
@@ -867,90 +841,147 @@ static struct snd_soc_device *wm8750_socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
- * WM8731 2 wire address is determined by GPIO5
+ * WM8750 2 wire address is determined by GPIO5
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-static struct i2c_driver wm8750_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8750_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8750_socdev;
- struct wm8750_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->codec;
- struct i2c_client *i2c;
int ret;
- if (addr != setup->i2c_address)
- return -ENODEV;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
- if (i2c == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = i2c_attach_client(i2c);
- if (ret < 0) {
- err("failed to attach codec at addr %x\n", addr);
- goto err;
- }
-
ret = wm8750_init(socdev);
- if (ret < 0) {
- err("failed to initialise WM8750\n");
- goto err;
- }
- return ret;
+ if (ret < 0)
+ pr_err("failed to initialise WM8750\n");
-err:
- kfree(codec);
- kfree(i2c);
return ret;
}
-static int wm8750_i2c_detach(struct i2c_client *client)
+static int wm8750_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
- i2c_detach_client(client);
kfree(codec->reg_cache);
- kfree(client);
return 0;
}
-static int wm8750_i2c_attach(struct i2c_adapter *adap)
-{
- return i2c_probe(adap, &addr_data, wm8750_codec_probe);
-}
+static const struct i2c_device_id wm8750_i2c_id[] = {
+ { "wm8750", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
-/* corgi i2c codec control layer */
static struct i2c_driver wm8750_i2c_driver = {
.driver = {
.name = "WM8750 I2C Codec",
.owner = THIS_MODULE,
},
- .id = I2C_DRIVERID_WM8750,
- .attach_adapter = wm8750_i2c_attach,
- .detach_client = wm8750_i2c_detach,
- .command = NULL,
+ .probe = wm8750_i2c_probe,
+ .remove = wm8750_i2c_remove,
+ .id_table = wm8750_i2c_id,
};
-static struct i2c_client client_template = {
- .name = "WM8750",
- .driver = &wm8750_i2c_driver,
+static int wm8750_add_i2c_device(struct platform_device *pdev,
+ const struct wm8750_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8750_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8750", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8750_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8750_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8750_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8750_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8750\n");
+
+ return ret;
+}
+
+static int __devexit wm8750_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8750_spi_driver = {
+ .driver = {
+ .name = "wm8750",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8750_spi_probe,
+ .remove = __devexit_p(wm8750_spi_remove),
};
+
+static int wm8750_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
#endif
static int wm8750_probe(struct platform_device *pdev)
@@ -959,9 +990,9 @@ static int wm8750_probe(struct platform_device *pdev)
struct wm8750_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec;
struct wm8750_priv *wm8750;
- int ret = 0;
+ int ret;
- info("WM8750 Audio Codec %s", WM8750_VERSION);
+ pr_info("WM8750 Audio Codec %s", WM8750_VERSION);
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
@@ -980,18 +1011,27 @@ static int wm8750_probe(struct platform_device *pdev)
wm8750_socdev = socdev;
INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);
+ ret = -ENODEV;
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t)i2c_master_send;
- ret = i2c_add_driver(&wm8750_i2c_driver);
+ ret = wm8750_add_i2c_device(pdev, setup);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8750_spi_write;
+ ret = spi_register_driver(&wm8750_spi_driver);
if (ret != 0)
- printk(KERN_ERR "can't add i2c driver");
+ printk(KERN_ERR "can't add spi driver");
}
-#else
- /* Add other interfaces here */
#endif
+ if (ret != 0) {
+ kfree(codec->private_data);
+ kfree(codec);
+ }
return ret;
}
@@ -1021,13 +1061,17 @@ static int wm8750_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8750_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8750_spi_driver);
+#endif
kfree(codec->private_data);
kfree(codec);
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
index a97a54a6348..1dc100e19cf 100644
--- a/sound/soc/codecs/wm8750.h
+++ b/sound/soc/codecs/wm8750.h
@@ -58,10 +58,12 @@
#define WM8750_SYSCLK 0
struct wm8750_setup_data {
+ int spi;
+ int i2c_bus;
unsigned short i2c_address;
};
-extern struct snd_soc_codec_dai wm8750_dai;
+extern struct snd_soc_dai wm8750_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8750;
#endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index fb41826c4c4..d426eaa2218 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -2,8 +2,7 @@
* wm8753.c -- WM8753 ALSA Soc Audio driver
*
* Copyright 2003 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -34,13 +33,13 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -52,28 +51,8 @@
#include "wm8753.h"
-#define AUDIO_NAME "wm8753"
#define WM8753_VERSION "0.16"
-/*
- * Debug
- */
-
-#define WM8753_DEBUG 0
-
-#ifdef WM8753_DEBUG
-#define dbg(format, arg...) \
- printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
- printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
- printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
- printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
static int caps_charge = 2000;
module_param(caps_charge, int, 0);
MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@@ -260,28 +239,50 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
return 1;
}
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const unsigned int out_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ /* 0000000 - 0101111 = "Analogue mute" */
+ 0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0),
+};
+static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
static const struct snd_kcontrol_new wm8753_snd_controls[] = {
-SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
-
-SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),
-
-SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
-SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
-
-SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
-
-SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
-SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
-SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
-
-SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
-SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
-
-SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
-SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1),
+SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0,
+ adc_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0,
+ 127, 0, out_tlv),
+
+SOC_SINGLE_TLV("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R_TLV("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7,
+ 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4,
+ 7, 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7,
+ 1, voice_mix_tlv),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7,
+ 1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7,
+ 1, 0),
+
+SOC_SINGLE_TLV("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1, mix_tlv),
+SOC_SINGLE_TLV("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1,
+ voice_mix_tlv),
SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
SOC_ENUM("Bass Boost", wm8753_enum[0]),
@@ -291,10 +292,13 @@ 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_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_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_TLV("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0,
+ pga_tlv),
SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
@@ -326,8 +330,8 @@ SOC_ENUM("De-emphasis", wm8753_enum[8]),
SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
SOC_ENUM("Playback Phase", wm8753_enum[10]),
-SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
-SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
+SOC_SINGLE_TLV("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0, mic_preamp_tlv),
+SOC_SINGLE_TLV("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0, mic_preamp_tlv),
SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
@@ -523,7 +527,7 @@ SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_VMID("VREF"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* left mixer */
{"Left Mixer", "Left Playback Switch", "Left DAC"},
{"Left Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -578,7 +582,7 @@ static const char *audio_map[][3] = {
/* out 4 */
{"Out4 Mux", "VREF", "VREF"},
- {"Out4 Mux", "Capture ST", "Capture ST Mixer"},
+ {"Out4 Mux", "Capture ST", "Playback Mixer"},
{"Out4 Mux", "LOUT2", "LOUT2"},
{"Out 4", NULL, "Out4 Mux"},
{"OUT4", NULL, "Out 4"},
@@ -602,7 +606,7 @@ static const char *audio_map[][3] = {
/* Capture Right Mux */
{"Capture Right Mux", "PGA", "Right Capture Volume"},
{"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"},
- {"Capture Right Mux", "Sidetone", "Capture ST Mixer"},
+ {"Capture Right Mux", "Sidetone", "Playback Mixer"},
/* Mono Capture mixer-mux */
{"Capture Right Mixer", "Stereo", "Capture Right Mux"},
@@ -674,23 +678,14 @@ static const char *audio_map[][3] = {
/* ACOP */
{"ACOP", NULL, "ALC Mixer"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
static int wm8753_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+ ARRAY_SIZE(wm8753_dapm_widgets));
- /* set up the WM8753 audio map */
- for (i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -743,7 +738,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
-static int wm8753_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
u16 reg, enable;
@@ -866,7 +861,7 @@ static int get_coeff(int mclk, int rate)
/*
* Clock after PLL and dividers
*/
-static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -893,7 +888,7 @@ static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
/*
* Set's ADC and Voice DAC format.
*/
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -963,7 +958,7 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
/*
* Set's PCM dai fmt and BCLK.
*/
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1029,7 +1024,7 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1057,7 +1052,7 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
/*
* Set's HiFi DAC format.
*/
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1090,7 +1085,7 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
/*
* Set's I2S DAI format.
*/
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1198,7 +1193,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1213,7 +1208,7 @@ static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
@@ -1221,7 +1216,7 @@ static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1236,7 +1231,7 @@ static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1253,7 +1248,7 @@ static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8753_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
@@ -1274,29 +1269,29 @@ static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8753_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
wm8753_write(codec, WM8753_PWR1, 0x0001);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -1319,7 +1314,7 @@ static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
* 3. Voice disabled - HIFI over HIFI
* 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
*/
-static const struct snd_soc_codec_dai wm8753_all_dai[] = {
+static const struct snd_soc_dai wm8753_all_dai[] = {
/* DAI HiFi mode 1 */
{ .name = "WM8753 HiFi",
.id = 1,
@@ -1459,7 +1454,7 @@ static const struct snd_soc_codec_dai wm8753_all_dai[] = {
},
};
-struct snd_soc_codec_dai wm8753_dai[2];
+struct snd_soc_dai wm8753_dai[2];
EXPORT_SYMBOL_GPL(wm8753_dai);
static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
@@ -1500,7 +1495,7 @@ static void wm8753_work(struct work_struct *work)
{
struct snd_soc_codec *codec =
container_of(work, struct snd_soc_codec, delayed_work.work);
- wm8753_dapm_event(codec, codec->dapm_state);
+ wm8753_set_bias_level(codec, codec->bias_level);
}
static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
@@ -1512,7 +1507,7 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
if (!codec->card)
return 0;
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1537,12 +1532,12 @@ static int wm8753_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* charge wm8753 caps */
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D0;
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_ON;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(caps_charge));
}
@@ -1563,10 +1558,10 @@ static int wm8753_init(struct snd_soc_device *socdev)
codec->owner = THIS_MODULE;
codec->read = wm8753_read_reg_cache;
codec->write = wm8753_write;
- codec->dapm_event = wm8753_dapm_event;
+ codec->set_bias_level = wm8753_set_bias_level;
codec->dai = wm8753_dai;
codec->num_dai = 2;
- codec->reg_cache_size = sizeof(wm8753_reg);
+ codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
@@ -1584,8 +1579,8 @@ static int wm8753_init(struct snd_soc_device *socdev)
}
/* charge output caps */
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(caps_charge));
@@ -1641,88 +1636,145 @@ static struct snd_soc_device *wm8753_socdev;
* low = 0x1a
* high = 0x1b
*/
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8753_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8753_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8753_socdev;
- struct wm8753_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) {
- kfree(codec);
- return -ENOMEM;
- }
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = i2c_attach_client(i2c);
- if (ret < 0) {
- err("failed to attach codec at addr %x\n", addr);
- goto err;
- }
-
ret = wm8753_init(socdev);
- if (ret < 0) {
- err("failed to initialise WM8753\n");
- goto err;
- }
+ if (ret < 0)
+ pr_err("failed to initialise WM8753\n");
return ret;
-
-err:
- kfree(codec);
- kfree(i2c);
- return ret;
}
-static int wm8753_i2c_detach(struct i2c_client *client)
+static int wm8753_i2c_remove(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 wm8753_i2c_attach(struct i2c_adapter *adap)
-{
- return i2c_probe(adap, &addr_data, wm8753_codec_probe);
-}
+static const struct i2c_device_id wm8753_i2c_id[] = {
+ { "wm8753", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
-/* corgi i2c codec control layer */
static struct i2c_driver wm8753_i2c_driver = {
.driver = {
.name = "WM8753 I2C Codec",
.owner = THIS_MODULE,
},
- .id = I2C_DRIVERID_WM8753,
- .attach_adapter = wm8753_i2c_attach,
- .detach_client = wm8753_i2c_detach,
- .command = NULL,
+ .probe = wm8753_i2c_probe,
+ .remove = wm8753_i2c_remove,
+ .id_table = wm8753_i2c_id,
};
-static struct i2c_client client_template = {
- .name = "WM8753",
- .driver = &wm8753_i2c_driver,
+static int wm8753_add_i2c_device(struct platform_device *pdev,
+ const struct wm8753_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8753_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8753", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8753_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8753_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8753_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8753_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8753\n");
+
+ return ret;
+}
+
+static int __devexit wm8753_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8753_spi_driver = {
+ .driver = {
+ .name = "wm8753",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8753_spi_probe,
+ .remove = __devexit_p(wm8753_spi_remove),
};
+
+static int wm8753_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
#endif
+
static int wm8753_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -1731,7 +1783,7 @@ static int wm8753_probe(struct platform_device *pdev)
struct wm8753_priv *wm8753;
int ret = 0;
- info("WM8753 Audio Codec %s", WM8753_VERSION);
+ pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -1754,15 +1806,23 @@ static int wm8753_probe(struct platform_device *pdev)
#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(&wm8753_i2c_driver);
+ ret = wm8753_add_i2c_device(pdev, setup);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8753_spi_write;
+ ret = spi_register_driver(&wm8753_spi_driver);
if (ret != 0)
- printk(KERN_ERR "can't add i2c driver");
+ printk(KERN_ERR "can't add spi driver");
}
-#else
- /* Add other interfaces here */
#endif
+
+ if (ret != 0) {
+ kfree(codec->private_data);
+ kfree(codec);
+ }
return ret;
}
@@ -1792,13 +1852,17 @@ static int wm8753_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8753_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8753_spi_driver);
+#endif
kfree(codec->private_data);
kfree(codec);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 95e2a1f5316..f55704ce931 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -2,8 +2,7 @@
* wm8753.h -- audio driver for WM8753
*
* Copyright 2003 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -79,6 +78,8 @@
#define WM8753_ADCTL2 0x3f
struct wm8753_setup_data {
+ int spi;
+ int i2c_bus;
unsigned short i2c_address;
};
@@ -120,7 +121,7 @@ struct wm8753_setup_data {
#define WM8753_DAI_HIFI 0
#define WM8753_DAI_VOICE 1
-extern struct snd_soc_codec_dai wm8753_dai[2];
+extern struct snd_soc_dai wm8753_dai[2];
extern struct snd_soc_codec_device soc_codec_dev_wm8753;
#endif
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
new file mode 100644
index 00000000000..3b326c9b558
--- /dev/null
+++ b/sound/soc/codecs/wm8900.c
@@ -0,0 +1,1541 @@
+/*
+ * wm8900.c -- WM8900 ALSA Soc Audio driver
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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.
+ *
+ * TODO:
+ * - Tristating.
+ * - TDM.
+ * - Jack detect.
+ * - FLL source configuration, currently only MCLK is supported.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.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 <sound/tlv.h>
+
+#include "wm8900.h"
+
+/* WM8900 register space */
+#define WM8900_REG_RESET 0x0
+#define WM8900_REG_ID 0x0
+#define WM8900_REG_POWER1 0x1
+#define WM8900_REG_POWER2 0x2
+#define WM8900_REG_POWER3 0x3
+#define WM8900_REG_AUDIO1 0x4
+#define WM8900_REG_AUDIO2 0x5
+#define WM8900_REG_CLOCKING1 0x6
+#define WM8900_REG_CLOCKING2 0x7
+#define WM8900_REG_AUDIO3 0x8
+#define WM8900_REG_AUDIO4 0x9
+#define WM8900_REG_DACCTRL 0xa
+#define WM8900_REG_LDAC_DV 0xb
+#define WM8900_REG_RDAC_DV 0xc
+#define WM8900_REG_SIDETONE 0xd
+#define WM8900_REG_ADCCTRL 0xe
+#define WM8900_REG_LADC_DV 0xf
+#define WM8900_REG_RADC_DV 0x10
+#define WM8900_REG_GPIO 0x12
+#define WM8900_REG_INCTL 0x15
+#define WM8900_REG_LINVOL 0x16
+#define WM8900_REG_RINVOL 0x17
+#define WM8900_REG_INBOOSTMIX1 0x18
+#define WM8900_REG_INBOOSTMIX2 0x19
+#define WM8900_REG_ADCPATH 0x1a
+#define WM8900_REG_AUXBOOST 0x1b
+#define WM8900_REG_ADDCTL 0x1e
+#define WM8900_REG_FLLCTL1 0x24
+#define WM8900_REG_FLLCTL2 0x25
+#define WM8900_REG_FLLCTL3 0x26
+#define WM8900_REG_FLLCTL4 0x27
+#define WM8900_REG_FLLCTL5 0x28
+#define WM8900_REG_FLLCTL6 0x29
+#define WM8900_REG_LOUTMIXCTL1 0x2c
+#define WM8900_REG_ROUTMIXCTL1 0x2d
+#define WM8900_REG_BYPASS1 0x2e
+#define WM8900_REG_BYPASS2 0x2f
+#define WM8900_REG_AUXOUT_CTL 0x30
+#define WM8900_REG_LOUT1CTL 0x33
+#define WM8900_REG_ROUT1CTL 0x34
+#define WM8900_REG_LOUT2CTL 0x35
+#define WM8900_REG_ROUT2CTL 0x36
+#define WM8900_REG_HPCTL1 0x3a
+#define WM8900_REG_OUTBIASCTL 0x73
+
+#define WM8900_MAXREG 0x80
+
+#define WM8900_REG_ADDCTL_OUT1_DIS 0x80
+#define WM8900_REG_ADDCTL_OUT2_DIS 0x40
+#define WM8900_REG_ADDCTL_VMID_DIS 0x20
+#define WM8900_REG_ADDCTL_BIAS_SRC 0x10
+#define WM8900_REG_ADDCTL_VMID_SOFTST 0x04
+#define WM8900_REG_ADDCTL_TEMP_SD 0x02
+
+#define WM8900_REG_GPIO_TEMP_ENA 0x2
+
+#define WM8900_REG_POWER1_STARTUP_BIAS_ENA 0x0100
+#define WM8900_REG_POWER1_BIAS_ENA 0x0008
+#define WM8900_REG_POWER1_VMID_BUF_ENA 0x0004
+#define WM8900_REG_POWER1_FLL_ENA 0x0040
+
+#define WM8900_REG_POWER2_SYSCLK_ENA 0x8000
+#define WM8900_REG_POWER2_ADCL_ENA 0x0002
+#define WM8900_REG_POWER2_ADCR_ENA 0x0001
+
+#define WM8900_REG_POWER3_DACL_ENA 0x0002
+#define WM8900_REG_POWER3_DACR_ENA 0x0001
+
+#define WM8900_REG_AUDIO1_AIF_FMT_MASK 0x0018
+#define WM8900_REG_AUDIO1_LRCLK_INV 0x0080
+#define WM8900_REG_AUDIO1_BCLK_INV 0x0100
+
+#define WM8900_REG_CLOCKING1_BCLK_DIR 0x1
+#define WM8900_REG_CLOCKING1_MCLK_SRC 0x100
+#define WM8900_REG_CLOCKING1_BCLK_MASK (~0x01e)
+#define WM8900_REG_CLOCKING1_OPCLK_MASK (~0x7000)
+
+#define WM8900_REG_CLOCKING2_ADC_CLKDIV 0xe0
+#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
+
+#define WM8900_REG_DACCTRL_MUTE 0x004
+#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
+
+#define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800
+
+#define WM8900_REG_AUDIO4_DACLRC_DIR 0x0800
+
+#define WM8900_REG_FLLCTL1_OSC_ENA 0x100
+
+#define WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF 0x100
+
+#define WM8900_REG_HPCTL1_HP_IPSTAGE_ENA 0x80
+#define WM8900_REG_HPCTL1_HP_OPSTAGE_ENA 0x40
+#define WM8900_REG_HPCTL1_HP_CLAMP_IP 0x20
+#define WM8900_REG_HPCTL1_HP_CLAMP_OP 0x10
+#define WM8900_REG_HPCTL1_HP_SHORT 0x08
+#define WM8900_REG_HPCTL1_HP_SHORT2 0x04
+
+#define WM8900_LRC_MASK 0xfc00
+
+struct snd_soc_codec_device soc_codec_dev_wm8900;
+
+struct wm8900_priv {
+ u32 fll_in; /* FLL input frequency */
+ u32 fll_out; /* FLL output frequency */
+};
+
+/*
+ * wm8900 register cache. We can't read the entire register space and we
+ * have slow control buses so we cache the registers.
+ */
+static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
+ 0x8900, 0x0000,
+ 0xc000, 0x0000,
+ 0x4050, 0x4000,
+ 0x0008, 0x0000,
+ 0x0040, 0x0040,
+ 0x1004, 0x00c0,
+ 0x00c0, 0x0000,
+ 0x0100, 0x00c0,
+ 0x00c0, 0x0000,
+ 0xb001, 0x0000,
+ 0x0000, 0x0044,
+ 0x004c, 0x004c,
+ 0x0044, 0x0044,
+ 0x0000, 0x0044,
+ 0x0000, 0x0000,
+ 0x0002, 0x0000,
+ 0x0000, 0x0000,
+ 0x0000, 0x0000,
+ 0x0008, 0x0000,
+ 0x0000, 0x0008,
+ 0x0097, 0x0100,
+ 0x0000, 0x0000,
+ 0x0050, 0x0050,
+ 0x0055, 0x0055,
+ 0x0055, 0x0000,
+ 0x0000, 0x0079,
+ 0x0079, 0x0079,
+ 0x0079, 0x0000,
+ /* Remaining registers all zero */
+};
+
+/*
+ * read wm8900 register cache
+ */
+static inline unsigned int wm8900_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ BUG_ON(reg >= WM8900_MAXREG);
+
+ if (reg == WM8900_REG_ID)
+ return 0;
+
+ return cache[reg];
+}
+
+/*
+ * write wm8900 register cache
+ */
+static inline void wm8900_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+
+ BUG_ON(reg >= WM8900_MAXREG);
+
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8900 register space
+ */
+static int wm8900_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ if (value == wm8900_read_reg_cache(codec, reg))
+ return 0;
+
+ /* data is
+ * D15..D9 WM8900 register offset
+ * D8...D0 register data
+ */
+ data[0] = reg;
+ data[1] = value >> 8;
+ data[2] = value & 0x00ff;
+
+ wm8900_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 3) == 3)
+ return 0;
+ else
+ return -EIO;
+}
+
+/*
+ * Read from the wm8900.
+ */
+static unsigned int wm8900_chip_read(struct snd_soc_codec *codec, u8 reg)
+{
+ struct i2c_msg xfer[2];
+ u16 data;
+ int ret;
+ struct i2c_client *client = codec->control_data;
+
+ BUG_ON(reg != WM8900_REG_ID && reg != WM8900_REG_POWER1);
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = &reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 2;
+ xfer[1].buf = (u8 *)&data;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret != 2) {
+ printk(KERN_CRIT "i2c_transfer returned %d\n", ret);
+ return 0;
+ }
+
+ return (data >> 8) | ((data & 0xff) << 8);
+}
+
+/*
+ * Read from the WM8900 register space. Most registers can't be read
+ * and are therefore supplied from cache.
+ */
+static unsigned int wm8900_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ switch (reg) {
+ case WM8900_REG_ID:
+ return wm8900_chip_read(codec, reg);
+ default:
+ return wm8900_read_reg_cache(codec, reg);
+ }
+}
+
+static void wm8900_reset(struct snd_soc_codec *codec)
+{
+ wm8900_write(codec, WM8900_REG_RESET, 0);
+
+ memcpy(codec->reg_cache, wm8900_reg_defaults,
+ sizeof(codec->reg_cache));
+}
+
+static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 hpctl1 = wm8900_read(codec, WM8900_REG_HPCTL1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Clamp headphone outputs */
+ hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
+ WM8900_REG_HPCTL1_HP_CLAMP_OP;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable the input stage */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP;
+ hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT |
+ WM8900_REG_HPCTL1_HP_SHORT2 |
+ WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ msleep(400);
+
+ /* Enable the output stage */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
+ hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ /* Remove the shorts */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Short the output */
+ hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ /* Disable the output stage */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ /* Clamp the outputs and power down input */
+ hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
+ WM8900_REG_HPCTL1_HP_CLAMP_OP;
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
+ wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable everything */
+ wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+ break;
+
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_boost_tlv, -1200, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1);
+
+static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3600, 300, 0);
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+
+static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" };
+
+static const struct soc_enum mic_bias_level =
+SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt);
+
+static const char *dac_mute_rate_txt[] = { "Fast", "Slow" };
+
+static const struct soc_enum dac_mute_rate =
+SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 7, 2, dac_mute_rate_txt);
+
+static const char *dac_deemphasis_txt[] = {
+ "Disabled", "32kHz", "44.1kHz", "48kHz"
+};
+
+static const struct soc_enum dac_deemphasis =
+SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 4, 4, dac_deemphasis_txt);
+
+static const char *adc_hpf_cut_txt[] = {
+ "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"
+};
+
+static const struct soc_enum adc_hpf_cut =
+SOC_ENUM_SINGLE(WM8900_REG_ADCCTRL, 5, 4, adc_hpf_cut_txt);
+
+static const char *lr_txt[] = {
+ "Left", "Right"
+};
+
+static const struct soc_enum aifl_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 15, 2, lr_txt);
+
+static const struct soc_enum aifr_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 14, 2, lr_txt);
+
+static const struct soc_enum dacl_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 15, 2, lr_txt);
+
+static const struct soc_enum dacr_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 14, 2, lr_txt);
+
+static const char *sidetone_txt[] = {
+ "Disabled", "Left ADC", "Right ADC"
+};
+
+static const struct soc_enum dacl_sidetone =
+SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 2, 3, sidetone_txt);
+
+static const struct soc_enum dacr_sidetone =
+SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 0, 3, sidetone_txt);
+
+static const struct snd_kcontrol_new wm8900_snd_controls[] = {
+SOC_ENUM("Mic Bias Level", mic_bias_level),
+
+SOC_SINGLE_TLV("Left Input PGA Volume", WM8900_REG_LINVOL, 0, 31, 0,
+ in_pga_tlv),
+SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1),
+SOC_SINGLE("Left Input PGA ZC Switch", WM8900_REG_LINVOL, 7, 1, 0),
+
+SOC_SINGLE_TLV("Right Input PGA Volume", WM8900_REG_RINVOL, 0, 31, 0,
+ in_pga_tlv),
+SOC_SINGLE("Right Input PGA Switch", WM8900_REG_RINVOL, 6, 1, 1),
+SOC_SINGLE("Right Input PGA ZC Switch", WM8900_REG_RINVOL, 7, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1),
+SOC_ENUM("DAC Mute Rate", dac_mute_rate),
+SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemphasis),
+SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0),
+SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
+ 12, 1, 0),
+
+SOC_SINGLE("ADC HPF Switch", WM8900_REG_ADCCTRL, 8, 1, 0),
+SOC_ENUM("ADC HPF Cut-Off", adc_hpf_cut),
+SOC_DOUBLE("ADC Invert Switch", WM8900_REG_ADCCTRL, 1, 0, 1, 0),
+SOC_SINGLE_TLV("Left ADC Sidetone Volume", WM8900_REG_SIDETONE, 9, 12, 0,
+ adc_svol_tlv),
+SOC_SINGLE_TLV("Right ADC Sidetone Volume", WM8900_REG_SIDETONE, 5, 12, 0,
+ adc_svol_tlv),
+SOC_ENUM("Left Digital Audio Source", aifl_src),
+SOC_ENUM("Right Digital Audio Source", aifr_src),
+
+SOC_SINGLE_TLV("DAC Input Boost Volume", WM8900_REG_AUDIO2, 10, 4, 0,
+ dac_boost_tlv),
+SOC_ENUM("Left DAC Source", dacl_src),
+SOC_ENUM("Right DAC Source", dacr_src),
+SOC_ENUM("Left DAC Sidetone", dacl_sidetone),
+SOC_ENUM("Right DAC Sidetone", dacr_sidetone),
+SOC_DOUBLE("DAC Invert Switch", WM8900_REG_DACCTRL, 1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume",
+ WM8900_REG_LDAC_DV, WM8900_REG_RDAC_DV,
+ 1, 96, 0, dac_tlv),
+SOC_DOUBLE_R_TLV("Digital Capture Volume",
+ WM8900_REG_LADC_DV, WM8900_REG_RADC_DV, 1, 119, 0, adc_tlv),
+
+SOC_SINGLE_TLV("LINPUT3 Bypass Volume", WM8900_REG_LOUTMIXCTL1, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("RINPUT3 Bypass Volume", WM8900_REG_ROUTMIXCTL1, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("Left AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("Right AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 0, 7, 0,
+ out_mix_tlv),
+
+SOC_SINGLE_TLV("LeftIn to RightOut Mixer Volume", WM8900_REG_BYPASS1, 0, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("LeftIn to LeftOut Mixer Volume", WM8900_REG_BYPASS1, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("RightIn to LeftOut Mixer Volume", WM8900_REG_BYPASS2, 0, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("RightIn to RightOut Mixer Volume", WM8900_REG_BYPASS2, 4, 7, 0,
+ out_mix_tlv),
+
+SOC_SINGLE_TLV("IN2L Boost Volume", WM8900_REG_INBOOSTMIX1, 0, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("IN3L Boost Volume", WM8900_REG_INBOOSTMIX1, 4, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("IN2R Boost Volume", WM8900_REG_INBOOSTMIX2, 0, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("IN3R Boost Volume", WM8900_REG_INBOOSTMIX2, 4, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("Left AUX Boost Volume", WM8900_REG_AUXBOOST, 4, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("Right AUX Boost Volume", WM8900_REG_AUXBOOST, 0, 3, 0,
+ in_boost_tlv),
+
+SOC_DOUBLE_R_TLV("LINEOUT1 Volume", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+ 0, 63, 0, out_pga_tlv),
+SOC_DOUBLE_R("LINEOUT1 Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+ 6, 1, 1),
+SOC_DOUBLE_R("LINEOUT1 ZC Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+ 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("LINEOUT2 Volume",
+ WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL,
+ 0, 63, 0, out_pga_tlv),
+SOC_DOUBLE_R("LINEOUT2 Switch",
+ WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 6, 1, 1),
+SOC_DOUBLE_R("LINEOUT2 ZC Switch",
+ WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 7, 1, 0),
+SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1,
+ 0, 1, 1),
+
+};
+
+/* add non dapm controls */
+static int wm8900_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8900_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8900_dapm_loutput2_control =
+SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0);
+
+static const struct snd_kcontrol_new wm8900_dapm_routput2_control =
+SOC_DAPM_SINGLE("LINEOUT2R Switch", WM8900_REG_POWER3, 5, 1, 0);
+
+static const struct snd_kcontrol_new wm8900_loutmix_controls[] = {
+SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0),
+SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0),
+SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_routmix_controls[] = {
+SOC_DAPM_SINGLE("RINPUT3 Bypass Switch", WM8900_REG_ROUTMIXCTL1, 7, 1, 0),
+SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 3, 1, 0),
+SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 3, 1, 0),
+SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 7, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8900_REG_ROUTMIXCTL1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_linmix_controls[] = {
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INBOOSTMIX1, 2, 1, 1),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INBOOSTMIX1, 6, 1, 1),
+SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 6, 1, 1),
+SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_rinmix_controls[] = {
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INBOOSTMIX2, 2, 1, 1),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INBOOSTMIX2, 6, 1, 1),
+SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 2, 1, 1),
+SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_linpga_controls[] = {
+SOC_DAPM_SINGLE("LINPUT1 Switch", WM8900_REG_INCTL, 6, 1, 0),
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INCTL, 5, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INCTL, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_rinpga_controls[] = {
+SOC_DAPM_SINGLE("RINPUT1 Switch", WM8900_REG_INCTL, 2, 1, 0),
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0),
+};
+
+static const char *wm9700_lp_mux[] = { "Disabled", "Enabled" };
+
+static const struct soc_enum wm8900_lineout2_lp_mux =
+SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm9700_lp_mux);
+
+static const struct snd_kcontrol_new wm8900_lineout2_lp =
+SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux);
+
+static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
+
+/* Externally visible pins */
+SND_SOC_DAPM_OUTPUT("LINEOUT1L"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1R"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2L"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2R"),
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_VMID("VMID"),
+
+/* Input */
+SND_SOC_DAPM_MIXER("Left Input PGA", WM8900_REG_POWER2, 3, 0,
+ wm8900_linpga_controls,
+ ARRAY_SIZE(wm8900_linpga_controls)),
+SND_SOC_DAPM_MIXER("Right Input PGA", WM8900_REG_POWER2, 2, 0,
+ wm8900_rinpga_controls,
+ ARRAY_SIZE(wm8900_rinpga_controls)),
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0,
+ wm8900_linmix_controls,
+ ARRAY_SIZE(wm8900_linmix_controls)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", WM8900_REG_POWER2, 4, 0,
+ wm8900_rinmix_controls,
+ ARRAY_SIZE(wm8900_rinmix_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8900_REG_POWER1, 4, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8900_REG_POWER2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8900_REG_POWER2, 0, 0),
+
+/* Output */
+SND_SOC_DAPM_DAC("DACL", "Left HiFi Playback", WM8900_REG_POWER3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", "Right HiFi Playback", WM8900_REG_POWER3, 0, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone Amplifier", WM8900_REG_POWER3, 7, 0, NULL, 0,
+ wm8900_hp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT1R PGA", WM8900_REG_POWER2, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("LINEOUT2 LP", SND_SOC_NOPM, 0, 0, &wm8900_lineout2_lp),
+SND_SOC_DAPM_PGA("LINEOUT2L PGA", WM8900_REG_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2R PGA", WM8900_REG_POWER3, 5, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,
+ wm8900_loutmix_controls,
+ ARRAY_SIZE(wm8900_loutmix_controls)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8900_REG_POWER3, 2, 0,
+ wm8900_routmix_controls,
+ ARRAY_SIZE(wm8900_routmix_controls)),
+};
+
+/* Target, Path, Source */
+static const struct snd_soc_dapm_route audio_map[] = {
+/* Inputs */
+{"Left Input PGA", "LINPUT1 Switch", "LINPUT1"},
+{"Left Input PGA", "LINPUT2 Switch", "LINPUT2"},
+{"Left Input PGA", "LINPUT3 Switch", "LINPUT3"},
+
+{"Right Input PGA", "RINPUT1 Switch", "RINPUT1"},
+{"Right Input PGA", "RINPUT2 Switch", "RINPUT2"},
+{"Right Input PGA", "RINPUT3 Switch", "RINPUT3"},
+
+{"Left Input Mixer", "LINPUT2 Switch", "LINPUT2"},
+{"Left Input Mixer", "LINPUT3 Switch", "LINPUT3"},
+{"Left Input Mixer", "AUX Switch", "AUX"},
+{"Left Input Mixer", "Input PGA Switch", "Left Input PGA"},
+
+{"Right Input Mixer", "RINPUT2 Switch", "RINPUT2"},
+{"Right Input Mixer", "RINPUT3 Switch", "RINPUT3"},
+{"Right Input Mixer", "AUX Switch", "AUX"},
+{"Right Input Mixer", "Input PGA Switch", "Right Input PGA"},
+
+{"ADCL", NULL, "Left Input Mixer"},
+{"ADCR", NULL, "Right Input Mixer"},
+
+/* Outputs */
+{"LINEOUT1L", NULL, "LINEOUT1L PGA"},
+{"LINEOUT1L PGA", NULL, "Left Output Mixer"},
+{"LINEOUT1R", NULL, "LINEOUT1R PGA"},
+{"LINEOUT1R PGA", NULL, "Right Output Mixer"},
+
+{"LINEOUT2L PGA", NULL, "Left Output Mixer"},
+{"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"},
+{"LINEOUT2 LP", "Enabled", "Left Output Mixer"},
+{"LINEOUT2L", NULL, "LINEOUT2 LP"},
+
+{"LINEOUT2R PGA", NULL, "Right Output Mixer"},
+{"LINEOUT2 LP", "Disabled", "LINEOUT2R PGA"},
+{"LINEOUT2 LP", "Enabled", "Right Output Mixer"},
+{"LINEOUT2R", NULL, "LINEOUT2 LP"},
+
+{"Left Output Mixer", "LINPUT3 Bypass Switch", "LINPUT3"},
+{"Left Output Mixer", "AUX Bypass Switch", "AUX"},
+{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
+{"Left Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"},
+{"Left Output Mixer", "DACL Switch", "DACL"},
+
+{"Right Output Mixer", "RINPUT3 Bypass Switch", "RINPUT3"},
+{"Right Output Mixer", "AUX Bypass Switch", "AUX"},
+{"Right Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
+{"Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"},
+{"Right Output Mixer", "DACR Switch", "DACR"},
+
+/* Note that the headphone output stage needs to be connected
+ * externally to LINEOUT2 via DC blocking capacitors. Other
+ * configurations are not supported.
+ *
+ * Note also that left and right headphone paths are treated as a
+ * mono path.
+ */
+{"Headphone Amplifier", NULL, "LINEOUT2 LP"},
+{"Headphone Amplifier", NULL, "LINEOUT2 LP"},
+{"HP_L", NULL, "Headphone Amplifier"},
+{"HP_R", NULL, "Headphone Amplifier"},
+};
+
+static int wm8900_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets,
+ ARRAY_SIZE(wm8900_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int wm8900_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;
+ u16 reg;
+
+ reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ reg |= 0x20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ reg |= 0x40;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ reg |= 0x60;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8900_write(codec, WM8900_REG_AUDIO1, reg);
+
+ return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+ u16 fll_ratio;
+ u16 fllclk_div;
+ u16 fll_slow_lock_ref;
+ u16 n;
+ u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+ unsigned int div;
+
+ BUG_ON(!Fout);
+
+ /* The FLL must run at 90-100MHz which is then scaled down to
+ * the output value by FLLCLK_DIV. */
+ target = Fout;
+ div = 1;
+ while (target < 90000000) {
+ div *= 2;
+ target *= 2;
+ }
+
+ if (target > 100000000)
+ printk(KERN_WARNING "wm8900: FLL rate %d out of range, Fref=%d"
+ " Fout=%d\n", target, Fref, Fout);
+ if (div > 32) {
+ printk(KERN_ERR "wm8900: Invalid FLL division rate %u, "
+ "Fref=%d, Fout=%d, target=%d\n",
+ div, Fref, Fout, target);
+ return -EINVAL;
+ }
+
+ fll_div->fllclk_div = div >> 2;
+
+ if (Fref < 48000)
+ fll_div->fll_slow_lock_ref = 1;
+ else
+ fll_div->fll_slow_lock_ref = 0;
+
+ Ndiv = target / Fref;
+
+ if (Fref < 1000000)
+ fll_div->fll_ratio = 8;
+ else
+ fll_div->fll_ratio = 1;
+
+ fll_div->n = Ndiv / fll_div->fll_ratio;
+ Nmod = (target / fll_div->fll_ratio) % Fref;
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll_div->k = K / 10;
+
+ BUG_ON(target != Fout * (fll_div->fllclk_div << 2));
+ BUG_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n);
+
+ return 0;
+}
+
+static int wm8900_set_fll(struct snd_soc_codec *codec,
+ int fll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct wm8900_priv *wm8900 = codec->private_data;
+ struct _fll_div fll_div;
+ unsigned int reg;
+
+ if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out)
+ return 0;
+
+ /* The digital side should be disabled during any change. */
+ reg = wm8900_read(codec, WM8900_REG_POWER1);
+ wm8900_write(codec, WM8900_REG_POWER1,
+ reg & (~WM8900_REG_POWER1_FLL_ENA));
+
+ /* Disable the FLL? */
+ if (!freq_in || !freq_out) {
+ reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+ wm8900_write(codec, WM8900_REG_CLOCKING1,
+ reg & (~WM8900_REG_CLOCKING1_MCLK_SRC));
+
+ reg = wm8900_read(codec, WM8900_REG_FLLCTL1);
+ wm8900_write(codec, WM8900_REG_FLLCTL1,
+ reg & (~WM8900_REG_FLLCTL1_OSC_ENA));
+
+ wm8900->fll_in = freq_in;
+ wm8900->fll_out = freq_out;
+
+ return 0;
+ }
+
+ if (fll_factors(&fll_div, freq_in, freq_out) != 0)
+ goto reenable;
+
+ wm8900->fll_in = freq_in;
+ wm8900->fll_out = freq_out;
+
+ /* The osclilator *MUST* be enabled before we enable the
+ * digital circuit. */
+ wm8900_write(codec, WM8900_REG_FLLCTL1,
+ fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA);
+
+ wm8900_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
+ wm8900_write(codec, WM8900_REG_FLLCTL5,
+ (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f));
+
+ if (fll_div.k) {
+ wm8900_write(codec, WM8900_REG_FLLCTL2,
+ (fll_div.k >> 8) | 0x100);
+ wm8900_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
+ } else
+ wm8900_write(codec, WM8900_REG_FLLCTL2, 0);
+
+ if (fll_div.fll_slow_lock_ref)
+ wm8900_write(codec, WM8900_REG_FLLCTL6,
+ WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF);
+ else
+ wm8900_write(codec, WM8900_REG_FLLCTL6, 0);
+
+ reg = wm8900_read(codec, WM8900_REG_POWER1);
+ wm8900_write(codec, WM8900_REG_POWER1,
+ reg | WM8900_REG_POWER1_FLL_ENA);
+
+reenable:
+ reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+ wm8900_write(codec, WM8900_REG_CLOCKING1,
+ reg | WM8900_REG_CLOCKING1_MCLK_SRC);
+
+ return 0;
+}
+
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
+}
+
+static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ switch (div_id) {
+ case WM8900_BCLK_DIV:
+ reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+ wm8900_write(codec, WM8900_REG_CLOCKING1,
+ div | (reg & WM8900_REG_CLOCKING1_BCLK_MASK));
+ break;
+ case WM8900_OPCLK_DIV:
+ reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+ wm8900_write(codec, WM8900_REG_CLOCKING1,
+ div | (reg & WM8900_REG_CLOCKING1_OPCLK_MASK));
+ break;
+ case WM8900_DAC_LRCLK:
+ reg = wm8900_read(codec, WM8900_REG_AUDIO4);
+ wm8900_write(codec, WM8900_REG_AUDIO4,
+ div | (reg & WM8900_LRC_MASK));
+ break;
+ case WM8900_ADC_LRCLK:
+ reg = wm8900_read(codec, WM8900_REG_AUDIO3);
+ wm8900_write(codec, WM8900_REG_AUDIO3,
+ div | (reg & WM8900_LRC_MASK));
+ break;
+ case WM8900_DAC_CLKDIV:
+ reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
+ wm8900_write(codec, WM8900_REG_CLOCKING2,
+ div | (reg & WM8900_REG_CLOCKING2_DAC_CLKDIV));
+ break;
+ case WM8900_ADC_CLKDIV:
+ reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
+ wm8900_write(codec, WM8900_REG_CLOCKING2,
+ div | (reg & WM8900_REG_CLOCKING2_ADC_CLKDIV));
+ break;
+ case WM8900_LRCLK_MODE:
+ reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+ wm8900_write(codec, WM8900_REG_DACCTRL,
+ div | (reg & WM8900_REG_DACCTRL_AIF_LRCLKRATE));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int clocking1, aif1, aif3, aif4;
+
+ clocking1 = wm8900_read(codec, WM8900_REG_CLOCKING1);
+ aif1 = wm8900_read(codec, WM8900_REG_AUDIO1);
+ aif3 = wm8900_read(codec, WM8900_REG_AUDIO3);
+ aif4 = wm8900_read(codec, WM8900_REG_AUDIO4);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 |= 0x8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8900_write(codec, WM8900_REG_CLOCKING1, clocking1);
+ wm8900_write(codec, WM8900_REG_AUDIO1, aif1);
+ wm8900_write(codec, WM8900_REG_AUDIO3, aif3);
+ wm8900_write(codec, WM8900_REG_AUDIO4, aif4);
+
+ return 0;
+}
+
+static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+
+ if (mute)
+ reg |= WM8900_REG_DACCTRL_MUTE;
+ else
+ reg &= ~WM8900_REG_DACCTRL_MUTE;
+
+ wm8900_write(codec, WM8900_REG_DACCTRL, reg);
+
+ return 0;
+}
+
+#define WM8900_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define WM8900_PCM_FORMATS \
+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+struct snd_soc_dai wm8900_dai = {
+ .name = "WM8900 HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8900_RATES,
+ .formats = WM8900_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8900_RATES,
+ .formats = WM8900_PCM_FORMATS,
+ },
+ .ops = {
+ .hw_params = wm8900_hw_params,
+ },
+ .dai_ops = {
+ .set_clkdiv = wm8900_set_dai_clkdiv,
+ .set_pll = wm8900_set_dai_pll,
+ .set_fmt = wm8900_set_dai_fmt,
+ .digital_mute = wm8900_digital_mute,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8900_dai);
+
+static int wm8900_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* Enable thermal shutdown */
+ reg = wm8900_read(codec, WM8900_REG_GPIO);
+ wm8900_write(codec, WM8900_REG_GPIO,
+ reg | WM8900_REG_GPIO_TEMP_ENA);
+ reg = wm8900_read(codec, WM8900_REG_ADDCTL);
+ wm8900_write(codec, WM8900_REG_ADDCTL,
+ reg | WM8900_REG_ADDCTL_TEMP_SD);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* Charge capacitors if initial power up */
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* STARTUP_BIAS_ENA on */
+ wm8900_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+
+ /* Startup bias mode */
+ wm8900_write(codec, WM8900_REG_ADDCTL,
+ WM8900_REG_ADDCTL_BIAS_SRC |
+ WM8900_REG_ADDCTL_VMID_SOFTST);
+
+ /* VMID 2x50k */
+ wm8900_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1);
+
+ /* Allow capacitors to charge */
+ schedule_timeout_interruptible(msecs_to_jiffies(400));
+
+ /* Enable bias */
+ wm8900_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA |
+ WM8900_REG_POWER1_BIAS_ENA | 0x1);
+
+ wm8900_write(codec, WM8900_REG_ADDCTL, 0);
+
+ wm8900_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_BIAS_ENA | 0x1);
+ }
+
+ reg = wm8900_read(codec, WM8900_REG_POWER1);
+ wm8900_write(codec, WM8900_REG_POWER1,
+ (reg & WM8900_REG_POWER1_FLL_ENA) |
+ WM8900_REG_POWER1_BIAS_ENA | 0x1);
+ wm8900_write(codec, WM8900_REG_POWER2,
+ WM8900_REG_POWER2_SYSCLK_ENA);
+ wm8900_write(codec, WM8900_REG_POWER3, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Startup bias enable */
+ reg = wm8900_read(codec, WM8900_REG_POWER1);
+ wm8900_write(codec, WM8900_REG_POWER1,
+ reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+ wm8900_write(codec, WM8900_REG_ADDCTL,
+ WM8900_REG_ADDCTL_BIAS_SRC |
+ WM8900_REG_ADDCTL_VMID_SOFTST);
+
+ /* Discharge caps */
+ wm8900_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+ schedule_timeout_interruptible(msecs_to_jiffies(500));
+
+ /* Remove clamp */
+ wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+
+ /* Power down */
+ wm8900_write(codec, WM8900_REG_ADDCTL, 0);
+ wm8900_write(codec, WM8900_REG_POWER1, 0);
+ wm8900_write(codec, WM8900_REG_POWER2, 0);
+ wm8900_write(codec, WM8900_REG_POWER3, 0);
+
+ /* Need to let things settle before stopping the clock
+ * to ensure that restart works, see "Stopping the
+ * master clock" in the datasheet. */
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ wm8900_write(codec, WM8900_REG_POWER2,
+ WM8900_REG_POWER2_SYSCLK_ENA);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+static int wm8900_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;
+ struct wm8900_priv *wm8900 = codec->private_data;
+ int fll_out = wm8900->fll_out;
+ int fll_in = wm8900->fll_in;
+ int ret;
+
+ /* Stop the FLL in an orderly fashion */
+ ret = wm8900_set_fll(codec, 0, 0, 0);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to stop FLL\n");
+ return ret;
+ }
+
+ wm8900->fll_out = fll_out;
+ wm8900->fll_in = fll_in;
+
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8900_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8900_priv *wm8900 = codec->private_data;
+ u16 *cache;
+ int i, ret;
+
+ cache = kmemdup(codec->reg_cache, sizeof(wm8900_reg_defaults),
+ GFP_KERNEL);
+
+ wm8900_reset(codec);
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Restart the FLL? */
+ if (wm8900->fll_out) {
+ int fll_out = wm8900->fll_out;
+ int fll_in = wm8900->fll_in;
+
+ wm8900->fll_in = 0;
+ wm8900->fll_out = 0;
+
+ ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to restart FLL\n");
+ return ret;
+ }
+ }
+
+ if (cache) {
+ for (i = 0; i < WM8900_MAXREG; i++)
+ wm8900_write(codec, i, cache[i]);
+ kfree(cache);
+ } else
+ dev_err(&pdev->dev, "Unable to allocate register cache\n");
+
+ return 0;
+}
+
+/*
+ * initialise the WM8900 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8900_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+ unsigned int reg;
+ struct i2c_client *i2c_client = socdev->codec->control_data;
+
+ codec->name = "WM8900";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8900_read;
+ codec->write = wm8900_write;
+ codec->dai = &wm8900_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8900_MAXREG;
+ codec->reg_cache = kmemdup(wm8900_reg_defaults,
+ sizeof(wm8900_reg_defaults), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ reg = wm8900_read(codec, WM8900_REG_ID);
+ if (reg != 0x8900) {
+ dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n",
+ reg);
+ return -ENODEV;
+ }
+
+ codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+ if (codec->private_data == NULL) {
+ ret = -ENOMEM;
+ goto priv_err;
+ }
+
+ /* Read back from the chip */
+ reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
+ reg = (reg >> 12) & 0xf;
+ dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg);
+
+ wm8900_reset(codec);
+
+ /* Latch the volume update bits */
+ wm8900_write(codec, WM8900_REG_LINVOL,
+ wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
+ wm8900_write(codec, WM8900_REG_RINVOL,
+ wm8900_read(codec, WM8900_REG_RINVOL) | 0x100);
+ wm8900_write(codec, WM8900_REG_LOUT1CTL,
+ wm8900_read(codec, WM8900_REG_LOUT1CTL) | 0x100);
+ wm8900_write(codec, WM8900_REG_ROUT1CTL,
+ wm8900_read(codec, WM8900_REG_ROUT1CTL) | 0x100);
+ wm8900_write(codec, WM8900_REG_LOUT2CTL,
+ wm8900_read(codec, WM8900_REG_LOUT2CTL) | 0x100);
+ wm8900_write(codec, WM8900_REG_ROUT2CTL,
+ wm8900_read(codec, WM8900_REG_ROUT2CTL) | 0x100);
+ wm8900_write(codec, WM8900_REG_LDAC_DV,
+ wm8900_read(codec, WM8900_REG_LDAC_DV) | 0x100);
+ wm8900_write(codec, WM8900_REG_RDAC_DV,
+ wm8900_read(codec, WM8900_REG_RDAC_DV) | 0x100);
+ wm8900_write(codec, WM8900_REG_LADC_DV,
+ wm8900_read(codec, WM8900_REG_LADC_DV) | 0x100);
+ wm8900_write(codec, WM8900_REG_RADC_DV,
+ wm8900_read(codec, WM8900_REG_RADC_DV) | 0x100);
+
+ /* Set the DAC and mixer output bias */
+ wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
+
+ /* Register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
+ goto pcm_err;
+ }
+
+ /* Turn the chip on */
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8900_add_controls(codec);
+ wm8900_add_widgets(codec);
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+priv_err:
+ kfree(codec->private_data);
+ return ret;
+}
+
+static struct snd_soc_device *wm8900_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8900_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 wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = wm8900_socdev;
+ struct wm8900_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;
+
+ dev_err(&adap->dev, "Probe on %x\n", addr);
+
+ 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) {
+ dev_err(&adap->dev,
+ "failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = wm8900_init(socdev);
+ if (ret < 0) {
+ dev_err(&adap->dev, "failed to initialise WM8900\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int wm8900_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 wm8900_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, wm8900_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8900_i2c_driver = {
+ .driver = {
+ .name = "WM8900 I2C codec",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = wm8900_i2c_attach,
+ .detach_client = wm8900_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "WM8900",
+ .driver = &wm8900_i2c_driver,
+};
+#endif
+
+static int wm8900_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8900_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ dev_info(&pdev->dev, "WM8900 Audio Codec\n");
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ socdev->codec = codec;
+
+ codec->set_bias_level = wm8900_set_bias_level;
+
+ wm8900_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(&wm8900_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+#error Non-I2C interfaces not yet supported
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int wm8900_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8900_i2c_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8900 = {
+ .probe = wm8900_probe,
+ .remove = wm8900_remove,
+ .suspend = wm8900_suspend,
+ .resume = wm8900_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
+
+MODULE_DESCRIPTION("ASoC WM8900 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h
new file mode 100644
index 00000000000..ba450d99e90
--- /dev/null
+++ b/sound/soc/codecs/wm8900.h
@@ -0,0 +1,64 @@
+/*
+ * wm8900.h -- WM890 Soc Audio driver
+ *
+ * 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 _WM8900_H
+#define _WM8900_H
+
+#define WM8900_FLL 1
+
+#define WM8900_BCLK_DIV 1
+#define WM8900_ADC_CLKDIV 2
+#define WM8900_DAC_CLKDIV 3
+#define WM8900_ADC_LRCLK 4
+#define WM8900_DAC_LRCLK 5
+#define WM8900_OPCLK_DIV 6
+#define WM8900_LRCLK_MODE 7
+
+#define WM8900_BCLK_DIV_1 0x00
+#define WM8900_BCLK_DIV_1_5 0x02
+#define WM8900_BCLK_DIV_2 0x04
+#define WM8900_BCLK_DIV_3 0x06
+#define WM8900_BCLK_DIV_4 0x08
+#define WM8900_BCLK_DIV_5_5 0x0a
+#define WM8900_BCLK_DIV_6 0x0c
+#define WM8900_BCLK_DIV_8 0x0e
+#define WM8900_BCLK_DIV_11 0x10
+#define WM8900_BCLK_DIV_12 0x12
+#define WM8900_BCLK_DIV_16 0x14
+#define WM8900_BCLK_DIV_22 0x16
+#define WM8900_BCLK_DIV_24 0x18
+#define WM8900_BCLK_DIV_32 0x1a
+#define WM8900_BCLK_DIV_44 0x1c
+#define WM8900_BCLK_DIV_48 0x1e
+
+#define WM8900_ADC_CLKDIV_1 0x00
+#define WM8900_ADC_CLKDIV_1_5 0x20
+#define WM8900_ADC_CLKDIV_2 0x40
+#define WM8900_ADC_CLKDIV_3 0x60
+#define WM8900_ADC_CLKDIV_4 0x80
+#define WM8900_ADC_CLKDIV_5_5 0xa0
+#define WM8900_ADC_CLKDIV_6 0xc0
+
+#define WM8900_DAC_CLKDIV_1 0x00
+#define WM8900_DAC_CLKDIV_1_5 0x04
+#define WM8900_DAC_CLKDIV_2 0x08
+#define WM8900_DAC_CLKDIV_3 0x0c
+#define WM8900_DAC_CLKDIV_4 0x10
+#define WM8900_DAC_CLKDIV_5_5 0x14
+#define WM8900_DAC_CLKDIV_6 0x18
+
+#define WM8900_
+
+struct wm8900_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8900_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8900;
+
+#endif
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
new file mode 100644
index 00000000000..ce40d787760
--- /dev/null
+++ b/sound/soc/codecs/wm8903.c
@@ -0,0 +1,1813 @@
+/*
+ * wm8903.c -- WM8903 ALSA SoC Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics
+ *
+ * Author: Mark Brown <broonie@opensource.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.
+ *
+ * TODO:
+ * - TDM mode configuration.
+ * - Mic detect.
+ * - Digital microphone support.
+ * - Interrupt support (mic detect and sequencer).
+ */
+
+#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/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8903.h"
+
+struct wm8903_priv {
+ int sysclk;
+
+ /* Reference counts */
+ int charge_pump_users;
+ int class_w_users;
+ int playback_active;
+ int capture_active;
+
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+/* Register defaults at reset */
+static u16 wm8903_reg_defaults[] = {
+ 0x8903, /* R0 - SW Reset and ID */
+ 0x0000, /* R1 - Revision Number */
+ 0x0000, /* R2 */
+ 0x0000, /* R3 */
+ 0x0018, /* R4 - Bias Control 0 */
+ 0x0000, /* R5 - VMID Control 0 */
+ 0x0000, /* R6 - Mic Bias Control 0 */
+ 0x0000, /* R7 */
+ 0x0001, /* R8 - Analogue DAC 0 */
+ 0x0000, /* R9 */
+ 0x0001, /* R10 - Analogue ADC 0 */
+ 0x0000, /* R11 */
+ 0x0000, /* R12 - Power Management 0 */
+ 0x0000, /* R13 - Power Management 1 */
+ 0x0000, /* R14 - Power Management 2 */
+ 0x0000, /* R15 - Power Management 3 */
+ 0x0000, /* R16 - Power Management 4 */
+ 0x0000, /* R17 - Power Management 5 */
+ 0x0000, /* R18 - Power Management 6 */
+ 0x0000, /* R19 */
+ 0x0400, /* R20 - Clock Rates 0 */
+ 0x0D07, /* R21 - Clock Rates 1 */
+ 0x0000, /* R22 - Clock Rates 2 */
+ 0x0000, /* R23 */
+ 0x0050, /* R24 - Audio Interface 0 */
+ 0x0242, /* R25 - Audio Interface 1 */
+ 0x0008, /* R26 - Audio Interface 2 */
+ 0x0022, /* R27 - Audio Interface 3 */
+ 0x0000, /* R28 */
+ 0x0000, /* R29 */
+ 0x00C0, /* R30 - DAC Digital Volume Left */
+ 0x00C0, /* R31 - DAC Digital Volume Right */
+ 0x0000, /* R32 - DAC Digital 0 */
+ 0x0000, /* R33 - DAC Digital 1 */
+ 0x0000, /* R34 */
+ 0x0000, /* R35 */
+ 0x00C0, /* R36 - ADC Digital Volume Left */
+ 0x00C0, /* R37 - ADC Digital Volume Right */
+ 0x0000, /* R38 - ADC Digital 0 */
+ 0x0073, /* R39 - Digital Microphone 0 */
+ 0x09BF, /* R40 - DRC 0 */
+ 0x3241, /* R41 - DRC 1 */
+ 0x0020, /* R42 - DRC 2 */
+ 0x0000, /* R43 - DRC 3 */
+ 0x0085, /* R44 - Analogue Left Input 0 */
+ 0x0085, /* R45 - Analogue Right Input 0 */
+ 0x0044, /* R46 - Analogue Left Input 1 */
+ 0x0044, /* R47 - Analogue Right Input 1 */
+ 0x0000, /* R48 */
+ 0x0000, /* R49 */
+ 0x0008, /* R50 - Analogue Left Mix 0 */
+ 0x0004, /* R51 - Analogue Right Mix 0 */
+ 0x0000, /* R52 - Analogue Spk Mix Left 0 */
+ 0x0000, /* R53 - Analogue Spk Mix Left 1 */
+ 0x0000, /* R54 - Analogue Spk Mix Right 0 */
+ 0x0000, /* R55 - Analogue Spk Mix Right 1 */
+ 0x0000, /* R56 */
+ 0x002D, /* R57 - Analogue OUT1 Left */
+ 0x002D, /* R58 - Analogue OUT1 Right */
+ 0x0039, /* R59 - Analogue OUT2 Left */
+ 0x0039, /* R60 - Analogue OUT2 Right */
+ 0x0100, /* R61 */
+ 0x0139, /* R62 - Analogue OUT3 Left */
+ 0x0139, /* R63 - Analogue OUT3 Right */
+ 0x0000, /* R64 */
+ 0x0000, /* R65 - Analogue SPK Output Control 0 */
+ 0x0000, /* R66 */
+ 0x0010, /* R67 - DC Servo 0 */
+ 0x0100, /* R68 */
+ 0x00A4, /* R69 - DC Servo 2 */
+ 0x0807, /* R70 */
+ 0x0000, /* R71 */
+ 0x0000, /* R72 */
+ 0x0000, /* R73 */
+ 0x0000, /* R74 */
+ 0x0000, /* R75 */
+ 0x0000, /* R76 */
+ 0x0000, /* R77 */
+ 0x0000, /* R78 */
+ 0x000E, /* R79 */
+ 0x0000, /* R80 */
+ 0x0000, /* R81 */
+ 0x0000, /* R82 */
+ 0x0000, /* R83 */
+ 0x0000, /* R84 */
+ 0x0000, /* R85 */
+ 0x0000, /* R86 */
+ 0x0006, /* R87 */
+ 0x0000, /* R88 */
+ 0x0000, /* R89 */
+ 0x0000, /* R90 - Analogue HP 0 */
+ 0x0060, /* R91 */
+ 0x0000, /* R92 */
+ 0x0000, /* R93 */
+ 0x0000, /* R94 - Analogue Lineout 0 */
+ 0x0060, /* R95 */
+ 0x0000, /* R96 */
+ 0x0000, /* R97 */
+ 0x0000, /* R98 - Charge Pump 0 */
+ 0x1F25, /* R99 */
+ 0x2B19, /* R100 */
+ 0x01C0, /* R101 */
+ 0x01EF, /* R102 */
+ 0x2B00, /* R103 */
+ 0x0000, /* R104 - Class W 0 */
+ 0x01C0, /* R105 */
+ 0x1C10, /* R106 */
+ 0x0000, /* R107 */
+ 0x0000, /* R108 - Write Sequencer 0 */
+ 0x0000, /* R109 - Write Sequencer 1 */
+ 0x0000, /* R110 - Write Sequencer 2 */
+ 0x0000, /* R111 - Write Sequencer 3 */
+ 0x0000, /* R112 - Write Sequencer 4 */
+ 0x0000, /* R113 */
+ 0x0000, /* R114 - Control Interface */
+ 0x0000, /* R115 */
+ 0x00A8, /* R116 - GPIO Control 1 */
+ 0x00A8, /* R117 - GPIO Control 2 */
+ 0x00A8, /* R118 - GPIO Control 3 */
+ 0x0220, /* R119 - GPIO Control 4 */
+ 0x01A0, /* R120 - GPIO Control 5 */
+ 0x0000, /* R121 - Interrupt Status 1 */
+ 0xFFFF, /* R122 - Interrupt Status 1 Mask */
+ 0x0000, /* R123 - Interrupt Polarity 1 */
+ 0x0000, /* R124 */
+ 0x0003, /* R125 */
+ 0x0000, /* R126 - Interrupt Control */
+ 0x0000, /* R127 */
+ 0x0005, /* R128 */
+ 0x0000, /* R129 - Control Interface Test 1 */
+ 0x0000, /* R130 */
+ 0x0000, /* R131 */
+ 0x0000, /* R132 */
+ 0x0000, /* R133 */
+ 0x0000, /* R134 */
+ 0x03FF, /* R135 */
+ 0x0007, /* R136 */
+ 0x0040, /* R137 */
+ 0x0000, /* R138 */
+ 0x0000, /* R139 */
+ 0x0000, /* R140 */
+ 0x0000, /* R141 */
+ 0x0000, /* R142 */
+ 0x0000, /* R143 */
+ 0x0000, /* R144 */
+ 0x0000, /* R145 */
+ 0x0000, /* R146 */
+ 0x0000, /* R147 */
+ 0x4000, /* R148 */
+ 0x6810, /* R149 - Charge Pump Test 1 */
+ 0x0004, /* R150 */
+ 0x0000, /* R151 */
+ 0x0000, /* R152 */
+ 0x0000, /* R153 */
+ 0x0000, /* R154 */
+ 0x0000, /* R155 */
+ 0x0000, /* R156 */
+ 0x0000, /* R157 */
+ 0x0000, /* R158 */
+ 0x0000, /* R159 */
+ 0x0000, /* R160 */
+ 0x0000, /* R161 */
+ 0x0000, /* R162 */
+ 0x0000, /* R163 */
+ 0x0028, /* R164 - Clock Rate Test 4 */
+ 0x0004, /* R165 */
+ 0x0000, /* R166 */
+ 0x0060, /* R167 */
+ 0x0000, /* R168 */
+ 0x0000, /* R169 */
+ 0x0000, /* R170 */
+ 0x0000, /* R171 */
+ 0x0000, /* R172 - Analogue Output Bias 0 */
+};
+
+static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
+
+ return cache[reg];
+}
+
+static unsigned int wm8903_hw_read(struct snd_soc_codec *codec, u8 reg)
+{
+ struct i2c_msg xfer[2];
+ u16 data;
+ int ret;
+ struct i2c_client *client = codec->control_data;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = &reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 2;
+ xfer[1].buf = (u8 *)&data;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret != 2) {
+ pr_err("i2c_transfer returned %d\n", ret);
+ return 0;
+ }
+
+ return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static unsigned int wm8903_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WM8903_SW_RESET_AND_ID:
+ case WM8903_REVISION_NUMBER:
+ case WM8903_INTERRUPT_STATUS_1:
+ case WM8903_WRITE_SEQUENCER_4:
+ return wm8903_hw_read(codec, reg);
+
+ default:
+ return wm8903_read_reg_cache(codec, reg);
+ }
+}
+
+static void wm8903_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+
+ BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
+
+ switch (reg) {
+ case WM8903_SW_RESET_AND_ID:
+ case WM8903_REVISION_NUMBER:
+ break;
+
+ default:
+ cache[reg] = value;
+ break;
+ }
+}
+
+static int wm8903_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ wm8903_write_reg_cache(codec, reg, value);
+
+ /* Data format is 1 byte of address followed by 2 bytes of data */
+ data[0] = reg;
+ data[1] = (value >> 8) & 0xff;
+ data[2] = value & 0xff;
+
+ if (codec->hw_write(codec->control_data, data, 3) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
+{
+ u16 reg[5];
+ struct i2c_client *i2c = codec->control_data;
+
+ BUG_ON(start > 48);
+
+ /* Enable the sequencer */
+ reg[0] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_0);
+ reg[0] |= WM8903_WSEQ_ENA;
+ wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
+
+ dev_dbg(&i2c->dev, "Starting sequence at %d\n", start);
+
+ wm8903_write(codec, WM8903_WRITE_SEQUENCER_3,
+ start | WM8903_WSEQ_START);
+
+ /* Wait for it to complete. If we have the interrupt wired up then
+ * we could block waiting for an interrupt, though polling may still
+ * be desirable for diagnostic purposes.
+ */
+ do {
+ msleep(10);
+
+ reg[4] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_4);
+ } while (reg[4] & WM8903_WSEQ_BUSY);
+
+ dev_dbg(&i2c->dev, "Sequence complete\n");
+
+ /* Disable the sequencer again */
+ wm8903_write(codec, WM8903_WRITE_SEQUENCER_0,
+ reg[0] & ~WM8903_WSEQ_ENA);
+
+ return 0;
+}
+
+static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
+{
+ int i;
+
+ /* There really ought to be something better we can do here :/ */
+ for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
+ cache[i] = wm8903_hw_read(codec, i);
+}
+
+static void wm8903_reset(struct snd_soc_codec *codec)
+{
+ wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+}
+
+#define WM8903_OUTPUT_SHORT 0x8
+#define WM8903_OUTPUT_OUT 0x4
+#define WM8903_OUTPUT_INT 0x2
+#define WM8903_OUTPUT_IN 0x1
+
+/*
+ * Event for headphone and line out amplifier power changes. Special
+ * power up/down sequences are required in order to maximise pop/click
+ * performance.
+ */
+static int wm8903_output_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm8903_priv *wm8903 = codec->private_data;
+ struct i2c_client *i2c = codec->control_data;
+ u16 val;
+ u16 reg;
+ int shift;
+ u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0);
+
+ switch (w->reg) {
+ case WM8903_POWER_MANAGEMENT_2:
+ reg = WM8903_ANALOGUE_HP_0;
+ break;
+ case WM8903_POWER_MANAGEMENT_3:
+ reg = WM8903_ANALOGUE_LINEOUT_0;
+ break;
+ default:
+ BUG();
+ }
+
+ switch (w->shift) {
+ case 0:
+ shift = 0;
+ break;
+ case 1:
+ shift = 4;
+ break;
+ default:
+ BUG();
+ }
+
+ if (event & SND_SOC_DAPM_PRE_PMU) {
+ val = wm8903_read(codec, reg);
+
+ /* Short the output */
+ val &= ~(WM8903_OUTPUT_SHORT << shift);
+ wm8903_write(codec, reg, val);
+
+ wm8903->charge_pump_users++;
+
+ dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
+ wm8903->charge_pump_users);
+
+ if (wm8903->charge_pump_users == 1) {
+ dev_dbg(&i2c->dev, "Enabling charge pump\n");
+ wm8903_write(codec, WM8903_CHARGE_PUMP_0,
+ cp_reg | WM8903_CP_ENA);
+ mdelay(4);
+ }
+ }
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ val = wm8903_read(codec, reg);
+
+ val |= (WM8903_OUTPUT_IN << shift);
+ wm8903_write(codec, reg, val);
+
+ val |= (WM8903_OUTPUT_INT << shift);
+ wm8903_write(codec, reg, val);
+
+ /* Turn on the output ENA_OUTP */
+ val |= (WM8903_OUTPUT_OUT << shift);
+ wm8903_write(codec, reg, val);
+
+ /* Remove the short */
+ val |= (WM8903_OUTPUT_SHORT << shift);
+ wm8903_write(codec, reg, val);
+ }
+
+ if (event & SND_SOC_DAPM_PRE_PMD) {
+ val = wm8903_read(codec, reg);
+
+ /* Short the output */
+ val &= ~(WM8903_OUTPUT_SHORT << shift);
+ wm8903_write(codec, reg, val);
+
+ /* Then disable the intermediate and output stages */
+ val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
+ WM8903_OUTPUT_IN) << shift);
+ wm8903_write(codec, reg, val);
+ }
+
+ if (event & SND_SOC_DAPM_POST_PMD) {
+ wm8903->charge_pump_users--;
+
+ dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
+ wm8903->charge_pump_users);
+
+ if (wm8903->charge_pump_users == 0) {
+ dev_dbg(&i2c->dev, "Disabling charge pump\n");
+ wm8903_write(codec, WM8903_CHARGE_PUMP_0,
+ cp_reg & ~WM8903_CP_ENA);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * When used with DAC outputs only the WM8903 charge pump supports
+ * operation in class W mode, providing very low power consumption
+ * when used with digital sources. Enable and disable this mode
+ * automatically depending on the mixer configuration.
+ *
+ * All the relevant controls are simple switches.
+ */
+static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = widget->codec;
+ struct wm8903_priv *wm8903 = codec->private_data;
+ struct i2c_client *i2c = codec->control_data;
+ u16 reg;
+ int ret;
+
+ reg = wm8903_read(codec, WM8903_CLASS_W_0);
+
+ /* Turn it off if we're about to enable bypass */
+ if (ucontrol->value.integer.value[0]) {
+ if (wm8903->class_w_users == 0) {
+ dev_dbg(&i2c->dev, "Disabling Class W\n");
+ wm8903_write(codec, WM8903_CLASS_W_0, reg &
+ ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V));
+ }
+ wm8903->class_w_users++;
+ }
+
+ /* Implement the change */
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ /* If we've just disabled the last bypass path turn Class W on */
+ if (!ucontrol->value.integer.value[0]) {
+ if (wm8903->class_w_users == 1) {
+ dev_dbg(&i2c->dev, "Enabling Class W\n");
+ wm8903_write(codec, WM8903_CLASS_W_0, reg |
+ WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
+ }
+ wm8903->class_w_users--;
+ }
+
+ dev_dbg(&i2c->dev, "Bypass use count now %d\n",
+ wm8903->class_w_users);
+
+ return ret;
+}
+
+#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+/* ALSA can only do steps of .01dB */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0);
+
+static const char *drc_slope_text[] = {
+ "1", "1/2", "1/4", "1/8", "1/16", "0"
+};
+
+static const struct soc_enum drc_slope_r0 =
+ SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text);
+
+static const struct soc_enum drc_slope_r1 =
+ SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text);
+
+static const char *drc_attack_text[] = {
+ "instantaneous",
+ "363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms",
+ "46.4ms", "92.8ms", "185.6ms"
+};
+
+static const struct soc_enum drc_attack =
+ SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+ "186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s",
+ "23.87s", "47.56s"
+};
+
+static const struct soc_enum drc_decay =
+ SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text);
+
+static const char *drc_ff_delay_text[] = {
+ "5 samples", "9 samples"
+};
+
+static const struct soc_enum drc_ff_delay =
+ SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text);
+
+static const char *drc_qr_decay_text[] = {
+ "0.725ms", "1.45ms", "5.8ms"
+};
+
+static const struct soc_enum drc_qr_decay =
+ SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text);
+
+static const char *drc_smoothing_text[] = {
+ "Low", "Medium", "High"
+};
+
+static const struct soc_enum drc_smoothing =
+ SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text);
+
+static const char *soft_mute_text[] = {
+ "Fast (fs/2)", "Slow (fs/32)"
+};
+
+static const struct soc_enum soft_mute =
+ SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text);
+
+static const char *mute_mode_text[] = {
+ "Hard", "Soft"
+};
+
+static const struct soc_enum mute_mode =
+ SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text);
+
+static const char *dac_deemphasis_text[] = {
+ "Disabled", "32kHz", "44.1kHz", "48kHz"
+};
+
+static const struct soc_enum dac_deemphasis =
+ SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text);
+
+static const char *companding_text[] = {
+ "ulaw", "alaw"
+};
+
+static const struct soc_enum dac_companding =
+ SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text);
+
+static const struct soc_enum adc_companding =
+ SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text);
+
+static const char *input_mode_text[] = {
+ "Single-Ended", "Differential Line", "Differential Mic"
+};
+
+static const struct soc_enum linput_mode_enum =
+ SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text);
+
+static const struct soc_enum rinput_mode_enum =
+ SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text);
+
+static const char *linput_mux_text[] = {
+ "IN1L", "IN2L", "IN3L"
+};
+
+static const struct soc_enum linput_enum =
+ SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text);
+
+static const struct soc_enum linput_inv_enum =
+ SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text);
+
+static const char *rinput_mux_text[] = {
+ "IN1R", "IN2R", "IN3R"
+};
+
+static const struct soc_enum rinput_enum =
+ SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text);
+
+static const struct soc_enum rinput_inv_enum =
+ SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text);
+
+
+static const struct snd_kcontrol_new wm8903_snd_controls[] = {
+
+/* Input PGAs - No TLV since the scale depends on PGA mode */
+SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0,
+ 7, 1, 1),
+SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0,
+ 0, 31, 0),
+SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1,
+ 6, 1, 0),
+
+SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0,
+ 7, 1, 1),
+SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0,
+ 0, 31, 0),
+SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1,
+ 6, 1, 0),
+
+/* ADCs */
+SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0),
+SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0),
+SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1),
+SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8903_DRC_3, 5, 124, 1,
+ drc_tlv_thresh),
+SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp),
+SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min),
+SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff_delay),
+SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0),
+SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0),
+SOC_SINGLE_TLV("DRC QR Threashold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max),
+SOC_ENUM("DRC QR Decay Rate", drc_qr_decay),
+SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0),
+SOC_ENUM("DRC Smoothing Threashold", drc_smoothing),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
+
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
+ WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+SOC_ENUM("ADC Companding Mode", adc_companding),
+SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
+
+/* DAC */
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
+ WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
+SOC_ENUM("DAC Soft Mute Rate", soft_mute),
+SOC_ENUM("DAC Mute Mode", mute_mode),
+SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
+SOC_ENUM("DAC De-emphasis", dac_deemphasis),
+SOC_SINGLE("DAC Sloping Stopband Filter Switch",
+ WM8903_DAC_DIGITAL_1, 11, 1, 0),
+SOC_ENUM("DAC Companding Mode", dac_companding),
+SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
+
+/* Headphones */
+SOC_DOUBLE_R("Headphone Switch",
+ WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+ 8, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch",
+ WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+ 6, 1, 0),
+SOC_DOUBLE_R_TLV("Headphone Volume",
+ WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+ 0, 63, 0, out_tlv),
+
+/* Line out */
+SOC_DOUBLE_R("Line Out Switch",
+ WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+ 8, 1, 1),
+SOC_DOUBLE_R("Line Out ZC Switch",
+ WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+ 6, 1, 0),
+SOC_DOUBLE_R_TLV("Line Out Volume",
+ WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+ 0, 63, 0, out_tlv),
+
+/* Speaker */
+SOC_DOUBLE_R("Speaker Switch",
+ WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch",
+ WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+ WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT,
+ 0, 63, 0, out_tlv),
+};
+
+static int wm8903_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8903_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new linput_mode_mux =
+ SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum);
+
+static const struct snd_kcontrol_new rinput_mode_mux =
+ SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum);
+
+static const struct snd_kcontrol_new linput_mux =
+ SOC_DAPM_ENUM("Left Input Mux", linput_enum);
+
+static const struct snd_kcontrol_new linput_inv_mux =
+ SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum);
+
+static const struct snd_kcontrol_new rinput_mux =
+ SOC_DAPM_ENUM("Right Input Mux", rinput_enum);
+
+static const struct snd_kcontrol_new rinput_inv_mux =
+ SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum);
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
+SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
+SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
+ 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
+ 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8903_MIC_BIAS_CONTROL_0, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux),
+SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0,
+ &linput_inv_mux),
+SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux),
+
+SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux),
+SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0,
+ &rinput_inv_mux),
+SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
+
+SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0,
+ left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0,
+ right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
+ 1, 0, NULL, 0, wm8903_output_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
+ 0, 0, NULL, 0, wm8903_output_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
+ NULL, 0, wm8903_output_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
+ NULL, 0, wm8903_output_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
+ NULL, 0),
+
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+ { "Left Input Mux", "IN1L", "IN1L" },
+ { "Left Input Mux", "IN2L", "IN2L" },
+ { "Left Input Mux", "IN3L", "IN3L" },
+
+ { "Left Input Inverting Mux", "IN1L", "IN1L" },
+ { "Left Input Inverting Mux", "IN2L", "IN2L" },
+ { "Left Input Inverting Mux", "IN3L", "IN3L" },
+
+ { "Right Input Mux", "IN1R", "IN1R" },
+ { "Right Input Mux", "IN2R", "IN2R" },
+ { "Right Input Mux", "IN3R", "IN3R" },
+
+ { "Right Input Inverting Mux", "IN1R", "IN1R" },
+ { "Right Input Inverting Mux", "IN2R", "IN2R" },
+ { "Right Input Inverting Mux", "IN3R", "IN3R" },
+
+ { "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" },
+ { "Left Input Mode Mux", "Differential Line",
+ "Left Input Mux" },
+ { "Left Input Mode Mux", "Differential Line",
+ "Left Input Inverting Mux" },
+ { "Left Input Mode Mux", "Differential Mic",
+ "Left Input Mux" },
+ { "Left Input Mode Mux", "Differential Mic",
+ "Left Input Inverting Mux" },
+
+ { "Right Input Mode Mux", "Single-Ended",
+ "Right Input Inverting Mux" },
+ { "Right Input Mode Mux", "Differential Line",
+ "Right Input Mux" },
+ { "Right Input Mode Mux", "Differential Line",
+ "Right Input Inverting Mux" },
+ { "Right Input Mode Mux", "Differential Mic",
+ "Right Input Mux" },
+ { "Right Input Mode Mux", "Differential Mic",
+ "Right Input Inverting Mux" },
+
+ { "Left Input PGA", NULL, "Left Input Mode Mux" },
+ { "Right Input PGA", NULL, "Right Input Mode Mux" },
+
+ { "ADCL", NULL, "Left Input PGA" },
+ { "ADCR", NULL, "Right Input PGA" },
+
+ { "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Left Output Mixer", "DACL Switch", "DACL" },
+ { "Left Output Mixer", "DACR Switch", "DACR" },
+
+ { "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Right Output Mixer", "DACL Switch", "DACL" },
+ { "Right Output Mixer", "DACR Switch", "DACR" },
+
+ { "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Left Speaker Mixer", "DACL Switch", "DACL" },
+ { "Left Speaker Mixer", "DACR Switch", "DACR" },
+
+ { "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Right Speaker Mixer", "DACL Switch", "DACL" },
+ { "Right Speaker Mixer", "DACR Switch", "DACR" },
+
+ { "Left Line Output PGA", NULL, "Left Output Mixer" },
+ { "Right Line Output PGA", NULL, "Right Output Mixer" },
+
+ { "Left Headphone Output PGA", NULL, "Left Output Mixer" },
+ { "Right Headphone Output PGA", NULL, "Right Output Mixer" },
+
+ { "Left Speaker PGA", NULL, "Left Speaker Mixer" },
+ { "Right Speaker PGA", NULL, "Right Speaker Mixer" },
+
+ { "HPOUTL", NULL, "Left Headphone Output PGA" },
+ { "HPOUTR", NULL, "Right Headphone Output PGA" },
+
+ { "LINEOUTL", NULL, "Left Line Output PGA" },
+ { "LINEOUTR", NULL, "Right Line Output PGA" },
+
+ { "LOP", NULL, "Left Speaker PGA" },
+ { "LON", NULL, "Left Speaker PGA" },
+
+ { "ROP", NULL, "Right Speaker PGA" },
+ { "RON", NULL, "Right Speaker PGA" },
+};
+
+static int wm8903_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets,
+ ARRAY_SIZE(wm8903_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int wm8903_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct i2c_client *i2c = codec->control_data;
+ u16 reg, reg2;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+ reg &= ~(WM8903_VMID_RES_MASK);
+ reg |= WM8903_VMID_RES_50K;
+ wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ wm8903_run_sequence(codec, 0);
+ wm8903_sync_reg_cache(codec, codec->reg_cache);
+
+ /* Enable low impedence charge pump output */
+ reg = wm8903_read(codec,
+ WM8903_CONTROL_INTERFACE_TEST_1);
+ wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+ reg | WM8903_TEST_KEY);
+ reg2 = wm8903_read(codec, WM8903_CHARGE_PUMP_TEST_1);
+ wm8903_write(codec, WM8903_CHARGE_PUMP_TEST_1,
+ reg2 | WM8903_CP_SW_KELVIN_MODE_MASK);
+ wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+ reg);
+
+ /* By default no bypass paths are enabled so
+ * enable Class W support.
+ */
+ dev_dbg(&i2c->dev, "Enabling Class W\n");
+ wm8903_write(codec, WM8903_CLASS_W_0, reg |
+ WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
+ }
+
+ reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+ reg &= ~(WM8903_VMID_RES_MASK);
+ reg |= WM8903_VMID_RES_250K;
+ wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ wm8903_run_sequence(codec, 32);
+ break;
+ }
+
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8903_priv *wm8903 = codec->private_data;
+
+ wm8903->sysclk = freq;
+
+ return 0;
+}
+
+static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
+
+ aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK |
+ WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif1 |= WM8903_LRCLK_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif1 |= WM8903_BCLK_DIR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= 0x3 | WM8903_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif1 |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8903_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8903_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8903_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+
+ return 0;
+}
+
+static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ reg = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+
+ if (mute)
+ reg |= WM8903_DAC_MUTE;
+ else
+ reg &= ~WM8903_DAC_MUTE;
+
+ wm8903_write(codec, WM8903_DAC_DIGITAL_1, reg);
+
+ return 0;
+}
+
+/* Lookup table for CLK_SYS/fs ratio. 256fs or more is recommended
+ * for optimal performance so we list the lower rates first and match
+ * on the last match we find. */
+static struct {
+ int div;
+ int rate;
+ int mode;
+ int mclk_div;
+} clk_sys_ratios[] = {
+ { 64, 0x0, 0x0, 1 },
+ { 68, 0x0, 0x1, 1 },
+ { 125, 0x0, 0x2, 1 },
+ { 128, 0x1, 0x0, 1 },
+ { 136, 0x1, 0x1, 1 },
+ { 192, 0x2, 0x0, 1 },
+ { 204, 0x2, 0x1, 1 },
+
+ { 64, 0x0, 0x0, 2 },
+ { 68, 0x0, 0x1, 2 },
+ { 125, 0x0, 0x2, 2 },
+ { 128, 0x1, 0x0, 2 },
+ { 136, 0x1, 0x1, 2 },
+ { 192, 0x2, 0x0, 2 },
+ { 204, 0x2, 0x1, 2 },
+
+ { 250, 0x2, 0x2, 1 },
+ { 256, 0x3, 0x0, 1 },
+ { 272, 0x3, 0x1, 1 },
+ { 384, 0x4, 0x0, 1 },
+ { 408, 0x4, 0x1, 1 },
+ { 375, 0x4, 0x2, 1 },
+ { 512, 0x5, 0x0, 1 },
+ { 544, 0x5, 0x1, 1 },
+ { 500, 0x5, 0x2, 1 },
+ { 768, 0x6, 0x0, 1 },
+ { 816, 0x6, 0x1, 1 },
+ { 750, 0x6, 0x2, 1 },
+ { 1024, 0x7, 0x0, 1 },
+ { 1088, 0x7, 0x1, 1 },
+ { 1000, 0x7, 0x2, 1 },
+ { 1408, 0x8, 0x0, 1 },
+ { 1496, 0x8, 0x1, 1 },
+ { 1536, 0x9, 0x0, 1 },
+ { 1632, 0x9, 0x1, 1 },
+ { 1500, 0x9, 0x2, 1 },
+
+ { 250, 0x2, 0x2, 2 },
+ { 256, 0x3, 0x0, 2 },
+ { 272, 0x3, 0x1, 2 },
+ { 384, 0x4, 0x0, 2 },
+ { 408, 0x4, 0x1, 2 },
+ { 375, 0x4, 0x2, 2 },
+ { 512, 0x5, 0x0, 2 },
+ { 544, 0x5, 0x1, 2 },
+ { 500, 0x5, 0x2, 2 },
+ { 768, 0x6, 0x0, 2 },
+ { 816, 0x6, 0x1, 2 },
+ { 750, 0x6, 0x2, 2 },
+ { 1024, 0x7, 0x0, 2 },
+ { 1088, 0x7, 0x1, 2 },
+ { 1000, 0x7, 0x2, 2 },
+ { 1408, 0x8, 0x0, 2 },
+ { 1496, 0x8, 0x1, 2 },
+ { 1536, 0x9, 0x0, 2 },
+ { 1632, 0x9, 0x1, 2 },
+ { 1500, 0x9, 0x2, 2 },
+};
+
+/* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */
+static struct {
+ int ratio;
+ int div;
+} bclk_divs[] = {
+ { 10, 0 },
+ { 15, 1 },
+ { 20, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 50, 5 },
+ { 55, 6 },
+ { 60, 7 },
+ { 80, 8 },
+ { 100, 9 },
+ { 110, 10 },
+ { 120, 11 },
+ { 160, 12 },
+ { 200, 13 },
+ { 220, 14 },
+ { 240, 15 },
+ { 250, 16 },
+ { 300, 17 },
+ { 320, 18 },
+ { 440, 19 },
+ { 480, 20 },
+};
+
+/* Sample rates for DSP */
+static struct {
+ int rate;
+ int value;
+} sample_rates[] = {
+ { 8000, 0 },
+ { 11025, 1 },
+ { 12000, 2 },
+ { 16000, 3 },
+ { 22050, 4 },
+ { 24000, 5 },
+ { 32000, 6 },
+ { 44100, 7 },
+ { 48000, 8 },
+ { 88200, 9 },
+ { 96000, 10 },
+ { 0, 0 },
+};
+
+static int wm8903_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8903_priv *wm8903 = codec->private_data;
+ struct i2c_client *i2c = codec->control_data;
+ struct snd_pcm_runtime *master_runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ wm8903->playback_active++;
+ else
+ wm8903->capture_active++;
+
+ /* The DAI has shared clocks so if we already have a playback or
+ * capture going then constrain this substream to match it.
+ */
+ if (wm8903->master_substream) {
+ master_runtime = wm8903->master_substream->runtime;
+
+ dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
+ master_runtime->sample_bits,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ master_runtime->rate,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ master_runtime->sample_bits,
+ master_runtime->sample_bits);
+
+ wm8903->slave_substream = substream;
+ } else
+ wm8903->master_substream = substream;
+
+ return 0;
+}
+
+static void wm8903_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8903_priv *wm8903 = codec->private_data;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ wm8903->playback_active--;
+ else
+ wm8903->capture_active--;
+
+ if (wm8903->master_substream == substream)
+ wm8903->master_substream = wm8903->slave_substream;
+
+ wm8903->slave_substream = NULL;
+}
+
+static int wm8903_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 wm8903_priv *wm8903 = codec->private_data;
+ struct i2c_client *i2c = codec->control_data;
+ int fs = params_rate(params);
+ int bclk;
+ int bclk_div;
+ int i;
+ int dsp_config;
+ int clk_config;
+ int best_val;
+ int cur_val;
+ int clk_sys;
+
+ u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
+ u16 aif2 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_2);
+ u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3);
+ u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0);
+ u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1);
+
+ if (substream == wm8903->slave_substream) {
+ dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+ return 0;
+ }
+
+ /* Configure sample rate logic for DSP - choose nearest rate */
+ dsp_config = 0;
+ best_val = abs(sample_rates[dsp_config].rate - fs);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ cur_val = abs(sample_rates[i].rate - fs);
+ if (cur_val <= best_val) {
+ dsp_config = i;
+ best_val = cur_val;
+ }
+ }
+
+ /* Constraints should stop us hitting this but let's make sure */
+ if (wm8903->capture_active)
+ switch (sample_rates[dsp_config].rate) {
+ case 88200:
+ case 96000:
+ dev_err(&i2c->dev, "%dHz unsupported by ADC\n",
+ fs);
+ return -EINVAL;
+
+ default:
+ break;
+ }
+
+ dev_dbg(&i2c->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate);
+ clock1 &= ~WM8903_SAMPLE_RATE_MASK;
+ clock1 |= sample_rates[dsp_config].value;
+
+ aif1 &= ~WM8903_AIF_WL_MASK;
+ bclk = 2 * fs;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bclk *= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bclk *= 20;
+ aif1 |= 0x4;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bclk *= 24;
+ aif1 |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bclk *= 32;
+ aif1 |= 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(&i2c->dev, "MCLK = %dHz, target sample rate = %dHz\n",
+ wm8903->sysclk, fs);
+
+ /* We may not have an MCLK which allows us to generate exactly
+ * the clock we want, particularly with USB derived inputs, so
+ * approximate.
+ */
+ clk_config = 0;
+ best_val = abs((wm8903->sysclk /
+ (clk_sys_ratios[0].mclk_div *
+ clk_sys_ratios[0].div)) - fs);
+ for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) {
+ cur_val = abs((wm8903->sysclk /
+ (clk_sys_ratios[i].mclk_div *
+ clk_sys_ratios[i].div)) - fs);
+
+ if (cur_val <= best_val) {
+ clk_config = i;
+ best_val = cur_val;
+ }
+ }
+
+ if (clk_sys_ratios[clk_config].mclk_div == 2) {
+ clock0 |= WM8903_MCLKDIV2;
+ clk_sys = wm8903->sysclk / 2;
+ } else {
+ clock0 &= ~WM8903_MCLKDIV2;
+ clk_sys = wm8903->sysclk;
+ }
+
+ clock1 &= ~(WM8903_CLK_SYS_RATE_MASK |
+ WM8903_CLK_SYS_MODE_MASK);
+ clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT;
+ clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT;
+
+ dev_dbg(&i2c->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n",
+ clk_sys_ratios[clk_config].rate,
+ clk_sys_ratios[clk_config].mode,
+ clk_sys_ratios[clk_config].div);
+
+ dev_dbg(&i2c->dev, "Actual CLK_SYS = %dHz\n", clk_sys);
+
+ /* We may not get quite the right frequency if using
+ * approximate clocks so look for the closest match that is
+ * higher than the target (we need to ensure that there enough
+ * BCLKs to clock out the samples).
+ */
+ bclk_div = 0;
+ best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk;
+ i = 1;
+ while (i < ARRAY_SIZE(bclk_divs)) {
+ cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ bclk_div = i;
+ best_val = cur_val;
+ i++;
+ }
+
+ aif2 &= ~WM8903_BCLK_DIV_MASK;
+ aif3 &= ~WM8903_LRCLK_RATE_MASK;
+
+ dev_dbg(&i2c->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n",
+ bclk_divs[bclk_div].ratio / 10, bclk,
+ (clk_sys * 10) / bclk_divs[bclk_div].ratio);
+
+ aif2 |= bclk_divs[bclk_div].div;
+ aif3 |= bclk / fs;
+
+ wm8903_write(codec, WM8903_CLOCK_RATES_0, clock0);
+ wm8903_write(codec, WM8903_CLOCK_RATES_1, clock1);
+ wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+ wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
+ wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+
+ return 0;
+}
+
+#define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8903_dai = {
+ .name = "WM8903",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8903_PLAYBACK_RATES,
+ .formats = WM8903_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8903_CAPTURE_RATES,
+ .formats = WM8903_FORMATS,
+ },
+ .ops = {
+ .startup = wm8903_startup,
+ .shutdown = wm8903_shutdown,
+ .hw_params = wm8903_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = wm8903_digital_mute,
+ .set_fmt = wm8903_set_dai_fmt,
+ .set_sysclk = wm8903_set_dai_sysclk
+ }
+};
+EXPORT_SYMBOL_GPL(wm8903_dai);
+
+static int wm8903_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;
+
+ wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8903_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c = codec->control_data;
+ int i;
+ u16 *reg_cache = codec->reg_cache;
+ u16 *tmp_cache = kmemdup(codec->reg_cache, sizeof(wm8903_reg_defaults),
+ GFP_KERNEL);
+
+ /* Bring the codec back up to standby first to minimise pop/clicks */
+ wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8903_set_bias_level(codec, codec->suspend_bias_level);
+
+ /* Sync back everything else */
+ if (tmp_cache) {
+ for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
+ if (tmp_cache[i] != reg_cache[i])
+ wm8903_write(codec, i, tmp_cache[i]);
+ } else {
+ dev_err(&i2c->dev, "Failed to allocate temporary cache\n");
+ }
+
+ return 0;
+}
+
+/*
+ * initialise the WM8903 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8903_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c = codec->control_data;
+ int ret = 0;
+ u16 val;
+
+ val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+ if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not a WM8903\n", val);
+ return -ENODEV;
+ }
+
+ codec->name = "WM8903";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8903_read;
+ codec->write = wm8903_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8903_set_bias_level;
+ codec->dai = &wm8903_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults);
+ codec->reg_cache = kmemdup(wm8903_reg_defaults,
+ sizeof(wm8903_reg_defaults),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ dev_err(&i2c->dev, "Failed to allocate register cache\n");
+ return -ENOMEM;
+ }
+
+ val = wm8903_read(codec, WM8903_REVISION_NUMBER);
+ dev_info(&i2c->dev, "WM8903 revision %d\n",
+ val & WM8903_CHIP_REV_MASK);
+
+ wm8903_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* SYSCLK is required for pretty much anything */
+ wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA);
+
+ /* power on device */
+ wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch volume update bits */
+ val = wm8903_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
+ val |= WM8903_ADCVU;
+ wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
+ wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
+
+ val = wm8903_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
+ val |= WM8903_DACVU;
+ wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
+ wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
+
+ val = wm8903_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
+ val |= WM8903_HPOUTVU;
+ wm8903_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
+ wm8903_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
+
+ val = wm8903_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
+ val |= WM8903_LINEOUTVU;
+ wm8903_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
+ wm8903_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
+
+ val = wm8903_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
+ val |= WM8903_SPKVU;
+ wm8903_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
+ wm8903_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
+
+ /* Enable DAC soft mute by default */
+ val = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+ val |= WM8903_DAC_MUTEMODE;
+ wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
+
+ wm8903_add_controls(codec);
+ wm8903_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "wm8903: 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 *wm8903_socdev;
+
+static int wm8903_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = wm8903_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = wm8903_init(socdev);
+ if (ret < 0)
+ dev_err(&i2c->dev, "Device initialisation failed\n");
+
+ return ret;
+}
+
+static int wm8903_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+/* i2c codec control layer */
+static const struct i2c_device_id wm8903_i2c_id[] = {
+ { "wm8903", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
+
+static struct i2c_driver wm8903_i2c_driver = {
+ .driver = {
+ .name = "WM8903",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8903_i2c_probe,
+ .remove = wm8903_i2c_remove,
+ .id_table = wm8903_i2c_id,
+};
+
+static int wm8903_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8903_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct wm8903_priv *wm8903;
+ struct i2c_board_info board_info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int ret = 0;
+
+ setup = socdev->codec_data;
+
+ if (!setup->i2c_address) {
+ dev_err(&pdev->dev, "No codec address provided\n");
+ return -ENODEV;
+ }
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
+ if (wm8903 == NULL) {
+ ret = -ENOMEM;
+ goto err_codec;
+ }
+
+ codec->private_data = wm8903;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ wm8903_socdev = socdev;
+
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = i2c_add_driver(&wm8903_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ goto err_priv;
+ } else {
+ memset(&board_info, 0, sizeof(board_info));
+ strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE);
+ board_info.addr = setup->i2c_address;
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "Can't get I2C bus %d\n",
+ setup->i2c_bus);
+ ret = -ENODEV;
+ goto err_adapter;
+ }
+
+ i2c_client = i2c_new_device(adapter, &board_info);
+ i2c_put_adapter(adapter);
+ if (i2c_client == NULL) {
+ dev_err(&pdev->dev,
+ "I2C driver registration failed\n");
+ ret = -ENODEV;
+ goto err_adapter;
+ }
+ }
+
+ return ret;
+
+err_adapter:
+ i2c_del_driver(&wm8903_i2c_driver);
+err_priv:
+ kfree(codec->private_data);
+err_codec:
+ kfree(codec);
+ return ret;
+}
+
+/* power down chip */
+static int wm8903_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ i2c_unregister_device(socdev->codec->control_data);
+ i2c_del_driver(&wm8903_i2c_driver);
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8903 = {
+ .probe = wm8903_probe,
+ .remove = wm8903_remove,
+ .suspend = wm8903_suspend,
+ .resume = wm8903_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
+
+MODULE_DESCRIPTION("ASoC WM8903 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
new file mode 100644
index 00000000000..cec622f2f66
--- /dev/null
+++ b/sound/soc/codecs/wm8903.h
@@ -0,0 +1,1463 @@
+/*
+ * wm8903.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM8903_H
+#define _WM8903_H
+
+#include <linux/i2c.h>
+
+extern struct snd_soc_dai wm8903_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8903;
+
+struct wm8903_setup_data {
+ int i2c_bus;
+ int i2c_address;
+};
+
+#define WM8903_MCLK_DIV_2 1
+#define WM8903_CLK_SYS 2
+#define WM8903_BCLK 3
+#define WM8903_LRCLK 4
+
+/*
+ * Register values.
+ */
+#define WM8903_SW_RESET_AND_ID 0x00
+#define WM8903_REVISION_NUMBER 0x01
+#define WM8903_BIAS_CONTROL_0 0x04
+#define WM8903_VMID_CONTROL_0 0x05
+#define WM8903_MIC_BIAS_CONTROL_0 0x06
+#define WM8903_ANALOGUE_DAC_0 0x08
+#define WM8903_ANALOGUE_ADC_0 0x0A
+#define WM8903_POWER_MANAGEMENT_0 0x0C
+#define WM8903_POWER_MANAGEMENT_1 0x0D
+#define WM8903_POWER_MANAGEMENT_2 0x0E
+#define WM8903_POWER_MANAGEMENT_3 0x0F
+#define WM8903_POWER_MANAGEMENT_4 0x10
+#define WM8903_POWER_MANAGEMENT_5 0x11
+#define WM8903_POWER_MANAGEMENT_6 0x12
+#define WM8903_CLOCK_RATES_0 0x14
+#define WM8903_CLOCK_RATES_1 0x15
+#define WM8903_CLOCK_RATES_2 0x16
+#define WM8903_AUDIO_INTERFACE_0 0x18
+#define WM8903_AUDIO_INTERFACE_1 0x19
+#define WM8903_AUDIO_INTERFACE_2 0x1A
+#define WM8903_AUDIO_INTERFACE_3 0x1B
+#define WM8903_DAC_DIGITAL_VOLUME_LEFT 0x1E
+#define WM8903_DAC_DIGITAL_VOLUME_RIGHT 0x1F
+#define WM8903_DAC_DIGITAL_0 0x20
+#define WM8903_DAC_DIGITAL_1 0x21
+#define WM8903_ADC_DIGITAL_VOLUME_LEFT 0x24
+#define WM8903_ADC_DIGITAL_VOLUME_RIGHT 0x25
+#define WM8903_ADC_DIGITAL_0 0x26
+#define WM8903_DIGITAL_MICROPHONE_0 0x27
+#define WM8903_DRC_0 0x28
+#define WM8903_DRC_1 0x29
+#define WM8903_DRC_2 0x2A
+#define WM8903_DRC_3 0x2B
+#define WM8903_ANALOGUE_LEFT_INPUT_0 0x2C
+#define WM8903_ANALOGUE_RIGHT_INPUT_0 0x2D
+#define WM8903_ANALOGUE_LEFT_INPUT_1 0x2E
+#define WM8903_ANALOGUE_RIGHT_INPUT_1 0x2F
+#define WM8903_ANALOGUE_LEFT_MIX_0 0x32
+#define WM8903_ANALOGUE_RIGHT_MIX_0 0x33
+#define WM8903_ANALOGUE_SPK_MIX_LEFT_0 0x34
+#define WM8903_ANALOGUE_SPK_MIX_LEFT_1 0x35
+#define WM8903_ANALOGUE_SPK_MIX_RIGHT_0 0x36
+#define WM8903_ANALOGUE_SPK_MIX_RIGHT_1 0x37
+#define WM8903_ANALOGUE_OUT1_LEFT 0x39
+#define WM8903_ANALOGUE_OUT1_RIGHT 0x3A
+#define WM8903_ANALOGUE_OUT2_LEFT 0x3B
+#define WM8903_ANALOGUE_OUT2_RIGHT 0x3C
+#define WM8903_ANALOGUE_OUT3_LEFT 0x3E
+#define WM8903_ANALOGUE_OUT3_RIGHT 0x3F
+#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41
+#define WM8903_DC_SERVO_0 0x43
+#define WM8903_DC_SERVO_2 0x45
+#define WM8903_ANALOGUE_HP_0 0x5A
+#define WM8903_ANALOGUE_LINEOUT_0 0x5E
+#define WM8903_CHARGE_PUMP_0 0x62
+#define WM8903_CLASS_W_0 0x68
+#define WM8903_WRITE_SEQUENCER_0 0x6C
+#define WM8903_WRITE_SEQUENCER_1 0x6D
+#define WM8903_WRITE_SEQUENCER_2 0x6E
+#define WM8903_WRITE_SEQUENCER_3 0x6F
+#define WM8903_WRITE_SEQUENCER_4 0x70
+#define WM8903_CONTROL_INTERFACE 0x72
+#define WM8903_GPIO_CONTROL_1 0x74
+#define WM8903_GPIO_CONTROL_2 0x75
+#define WM8903_GPIO_CONTROL_3 0x76
+#define WM8903_GPIO_CONTROL_4 0x77
+#define WM8903_GPIO_CONTROL_5 0x78
+#define WM8903_INTERRUPT_STATUS_1 0x79
+#define WM8903_INTERRUPT_STATUS_1_MASK 0x7A
+#define WM8903_INTERRUPT_POLARITY_1 0x7B
+#define WM8903_INTERRUPT_CONTROL 0x7E
+#define WM8903_CONTROL_INTERFACE_TEST_1 0x81
+#define WM8903_CHARGE_PUMP_TEST_1 0x95
+#define WM8903_CLOCK_RATE_TEST_4 0xA4
+#define WM8903_ANALOGUE_OUTPUT_BIAS_0 0xAC
+
+#define WM8903_REGISTER_COUNT 75
+#define WM8903_MAX_REGISTER 0xAC
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - SW Reset and ID
+ */
+#define WM8903_SW_RESET_DEV_ID1_MASK 0xFFFF /* SW_RESET_DEV_ID1 - [15:0] */
+#define WM8903_SW_RESET_DEV_ID1_SHIFT 0 /* SW_RESET_DEV_ID1 - [15:0] */
+#define WM8903_SW_RESET_DEV_ID1_WIDTH 16 /* SW_RESET_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Revision Number
+ */
+#define WM8903_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8903_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8903_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R4 (0x04) - Bias Control 0
+ */
+#define WM8903_POBCTRL 0x0010 /* POBCTRL */
+#define WM8903_POBCTRL_MASK 0x0010 /* POBCTRL */
+#define WM8903_POBCTRL_SHIFT 4 /* POBCTRL */
+#define WM8903_POBCTRL_WIDTH 1 /* POBCTRL */
+#define WM8903_ISEL_MASK 0x000C /* ISEL - [3:2] */
+#define WM8903_ISEL_SHIFT 2 /* ISEL - [3:2] */
+#define WM8903_ISEL_WIDTH 2 /* ISEL - [3:2] */
+#define WM8903_STARTUP_BIAS_ENA 0x0002 /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_MASK 0x0002 /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_SHIFT 1 /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+#define WM8903_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM8903_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM8903_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM8903_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R5 (0x05) - VMID Control 0
+ */
+#define WM8903_VMID_TIE_ENA 0x0080 /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_MASK 0x0080 /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_SHIFT 7 /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_WIDTH 1 /* VMID_TIE_ENA */
+#define WM8903_BUFIO_ENA 0x0040 /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_MASK 0x0040 /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_SHIFT 6 /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_WIDTH 1 /* BUFIO_ENA */
+#define WM8903_VMID_IO_ENA 0x0020 /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_MASK 0x0020 /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_SHIFT 5 /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_WIDTH 1 /* VMID_IO_ENA */
+#define WM8903_VMID_SOFT_MASK 0x0018 /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_SOFT_SHIFT 3 /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_SOFT_WIDTH 2 /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */
+#define WM8903_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */
+#define WM8903_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */
+#define WM8903_VMID_BUF_ENA 0x0001 /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_MASK 0x0001 /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_SHIFT 0 /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+
+#define WM8903_VMID_RES_50K 2
+#define WM8903_VMID_RES_250K 3
+#define WM8903_VMID_RES_5K 4
+
+/*
+ * R6 (0x06) - Mic Bias Control 0
+ */
+#define WM8903_MICDET_HYST_ENA 0x0080 /* MICDET_HYST_ENA */
+#define WM8903_MICDET_HYST_ENA_MASK 0x0080 /* MICDET_HYST_ENA */
+#define WM8903_MICDET_HYST_ENA_SHIFT 7 /* MICDET_HYST_ENA */
+#define WM8903_MICDET_HYST_ENA_WIDTH 1 /* MICDET_HYST_ENA */
+#define WM8903_MICDET_THR_MASK 0x0070 /* MICDET_THR - [6:4] */
+#define WM8903_MICDET_THR_SHIFT 4 /* MICDET_THR - [6:4] */
+#define WM8903_MICDET_THR_WIDTH 3 /* MICDET_THR - [6:4] */
+#define WM8903_MICSHORT_THR_MASK 0x000C /* MICSHORT_THR - [3:2] */
+#define WM8903_MICSHORT_THR_SHIFT 2 /* MICSHORT_THR - [3:2] */
+#define WM8903_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [3:2] */
+#define WM8903_MICDET_ENA 0x0002 /* MICDET_ENA */
+#define WM8903_MICDET_ENA_MASK 0x0002 /* MICDET_ENA */
+#define WM8903_MICDET_ENA_SHIFT 1 /* MICDET_ENA */
+#define WM8903_MICDET_ENA_WIDTH 1 /* MICDET_ENA */
+#define WM8903_MICBIAS_ENA 0x0001 /* MICBIAS_ENA */
+#define WM8903_MICBIAS_ENA_MASK 0x0001 /* MICBIAS_ENA */
+#define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */
+#define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */
+
+/*
+ * R8 (0x08) - Analogue DAC 0
+ */
+#define WM8903_DACBIAS_SEL_MASK 0x0018 /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACBIAS_SEL_SHIFT 3 /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACBIAS_SEL_WIDTH 2 /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACVMID_BIAS_SEL_MASK 0x0006 /* DACVMID_BIAS_SEL - [2:1] */
+#define WM8903_DACVMID_BIAS_SEL_SHIFT 1 /* DACVMID_BIAS_SEL - [2:1] */
+#define WM8903_DACVMID_BIAS_SEL_WIDTH 2 /* DACVMID_BIAS_SEL - [2:1] */
+
+/*
+ * R10 (0x0A) - Analogue ADC 0
+ */
+#define WM8903_ADC_OSR128 0x0001 /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_MASK 0x0001 /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_SHIFT 0 /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+
+/*
+ * R12 (0x0C) - Power Management 0
+ */
+#define WM8903_INL_ENA 0x0002 /* INL_ENA */
+#define WM8903_INL_ENA_MASK 0x0002 /* INL_ENA */
+#define WM8903_INL_ENA_SHIFT 1 /* INL_ENA */
+#define WM8903_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8903_INR_ENA 0x0001 /* INR_ENA */
+#define WM8903_INR_ENA_MASK 0x0001 /* INR_ENA */
+#define WM8903_INR_ENA_SHIFT 0 /* INR_ENA */
+#define WM8903_INR_ENA_WIDTH 1 /* INR_ENA */
+
+/*
+ * R13 (0x0D) - Power Management 1
+ */
+#define WM8903_MIXOUTL_ENA 0x0002 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_MASK 0x0002 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_SHIFT 1 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTR_ENA 0x0001 /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_MASK 0x0001 /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_SHIFT 0 /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */
+
+/*
+ * R14 (0x0E) - Power Management 2
+ */
+#define WM8903_HPL_PGA_ENA 0x0002 /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_MASK 0x0002 /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_SHIFT 1 /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_WIDTH 1 /* HPL_PGA_ENA */
+#define WM8903_HPR_PGA_ENA 0x0001 /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_MASK 0x0001 /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_SHIFT 0 /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_WIDTH 1 /* HPR_PGA_ENA */
+
+/*
+ * R15 (0x0F) - Power Management 3
+ */
+#define WM8903_LINEOUTL_PGA_ENA 0x0002 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_MASK 0x0002 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_SHIFT 1 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_WIDTH 1 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA 0x0001 /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_MASK 0x0001 /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_SHIFT 0 /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_WIDTH 1 /* LINEOUTR_PGA_ENA */
+
+/*
+ * R16 (0x10) - Power Management 4
+ */
+#define WM8903_MIXSPKL_ENA 0x0002 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_MASK 0x0002 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_SHIFT 1 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_WIDTH 1 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKR_ENA 0x0001 /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_MASK 0x0001 /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_SHIFT 0 /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_WIDTH 1 /* MIXSPKR_ENA */
+
+/*
+ * R17 (0x11) - Power Management 5
+ */
+#define WM8903_SPKL_ENA 0x0002 /* SPKL_ENA */
+#define WM8903_SPKL_ENA_MASK 0x0002 /* SPKL_ENA */
+#define WM8903_SPKL_ENA_SHIFT 1 /* SPKL_ENA */
+#define WM8903_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8903_SPKR_ENA 0x0001 /* SPKR_ENA */
+#define WM8903_SPKR_ENA_MASK 0x0001 /* SPKR_ENA */
+#define WM8903_SPKR_ENA_SHIFT 0 /* SPKR_ENA */
+#define WM8903_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+
+/*
+ * R18 (0x12) - Power Management 6
+ */
+#define WM8903_DACL_ENA 0x0008 /* DACL_ENA */
+#define WM8903_DACL_ENA_MASK 0x0008 /* DACL_ENA */
+#define WM8903_DACL_ENA_SHIFT 3 /* DACL_ENA */
+#define WM8903_DACL_ENA_WIDTH 1 /* DACL_ENA */
+#define WM8903_DACR_ENA 0x0004 /* DACR_ENA */
+#define WM8903_DACR_ENA_MASK 0x0004 /* DACR_ENA */
+#define WM8903_DACR_ENA_SHIFT 2 /* DACR_ENA */
+#define WM8903_DACR_ENA_WIDTH 1 /* DACR_ENA */
+#define WM8903_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8903_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8903_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8903_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8903_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8903_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8903_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8903_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R20 (0x14) - Clock Rates 0
+ */
+#define WM8903_MCLKDIV2 0x0001 /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_MASK 0x0001 /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_SHIFT 0 /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */
+
+/*
+ * R21 (0x15) - Clock Rates 1
+ */
+#define WM8903_CLK_SYS_RATE_MASK 0x3C00 /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_RATE_SHIFT 10 /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_MODE_MASK 0x0300 /* CLK_SYS_MODE - [9:8] */
+#define WM8903_CLK_SYS_MODE_SHIFT 8 /* CLK_SYS_MODE - [9:8] */
+#define WM8903_CLK_SYS_MODE_WIDTH 2 /* CLK_SYS_MODE - [9:8] */
+#define WM8903_SAMPLE_RATE_MASK 0x000F /* SAMPLE_RATE - [3:0] */
+#define WM8903_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [3:0] */
+#define WM8903_SAMPLE_RATE_WIDTH 4 /* SAMPLE_RATE - [3:0] */
+
+/*
+ * R22 (0x16) - Clock Rates 2
+ */
+#define WM8903_CLK_SYS_ENA 0x0004 /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_MASK 0x0004 /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_SHIFT 2 /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+#define WM8903_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+#define WM8903_TO_ENA 0x0001 /* TO_ENA */
+#define WM8903_TO_ENA_MASK 0x0001 /* TO_ENA */
+#define WM8903_TO_ENA_SHIFT 0 /* TO_ENA */
+#define WM8903_TO_ENA_WIDTH 1 /* TO_ENA */
+
+/*
+ * R24 (0x18) - Audio Interface 0
+ */
+#define WM8903_DACL_DATINV 0x1000 /* DACL_DATINV */
+#define WM8903_DACL_DATINV_MASK 0x1000 /* DACL_DATINV */
+#define WM8903_DACL_DATINV_SHIFT 12 /* DACL_DATINV */
+#define WM8903_DACL_DATINV_WIDTH 1 /* DACL_DATINV */
+#define WM8903_DACR_DATINV 0x0800 /* DACR_DATINV */
+#define WM8903_DACR_DATINV_MASK 0x0800 /* DACR_DATINV */
+#define WM8903_DACR_DATINV_SHIFT 11 /* DACR_DATINV */
+#define WM8903_DACR_DATINV_WIDTH 1 /* DACR_DATINV */
+#define WM8903_DAC_BOOST_MASK 0x0600 /* DAC_BOOST - [10:9] */
+#define WM8903_DAC_BOOST_SHIFT 9 /* DAC_BOOST - [10:9] */
+#define WM8903_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [10:9] */
+#define WM8903_LOOPBACK 0x0100 /* LOOPBACK */
+#define WM8903_LOOPBACK_MASK 0x0100 /* LOOPBACK */
+#define WM8903_LOOPBACK_SHIFT 8 /* LOOPBACK */
+#define WM8903_LOOPBACK_WIDTH 1 /* LOOPBACK */
+#define WM8903_AIFADCL_SRC 0x0080 /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_MASK 0x0080 /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_SHIFT 7 /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */
+#define WM8903_AIFADCR_SRC 0x0040 /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_MASK 0x0040 /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_SHIFT 6 /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */
+#define WM8903_AIFDACL_SRC 0x0020 /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_MASK 0x0020 /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_SHIFT 5 /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */
+#define WM8903_AIFDACR_SRC 0x0010 /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_MASK 0x0010 /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_SHIFT 4 /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */
+#define WM8903_ADC_COMP 0x0008 /* ADC_COMP */
+#define WM8903_ADC_COMP_MASK 0x0008 /* ADC_COMP */
+#define WM8903_ADC_COMP_SHIFT 3 /* ADC_COMP */
+#define WM8903_ADC_COMP_WIDTH 1 /* ADC_COMP */
+#define WM8903_ADC_COMPMODE 0x0004 /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_MASK 0x0004 /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_SHIFT 2 /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */
+#define WM8903_DAC_COMP 0x0002 /* DAC_COMP */
+#define WM8903_DAC_COMP_MASK 0x0002 /* DAC_COMP */
+#define WM8903_DAC_COMP_SHIFT 1 /* DAC_COMP */
+#define WM8903_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM8903_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+
+/*
+ * R25 (0x19) - Audio Interface 1
+ */
+#define WM8903_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFADC_TDM 0x0800 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_MASK 0x0800 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_SHIFT 11 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_CHAN 0x0400 /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_MASK 0x0400 /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_SHIFT 10 /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */
+#define WM8903_LRCLK_DIR 0x0200 /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_MASK 0x0200 /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_SHIFT 9 /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */
+#define WM8903_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */
+#define WM8903_BCLK_DIR 0x0040 /* BCLK_DIR */
+#define WM8903_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */
+#define WM8903_BCLK_DIR_SHIFT 6 /* BCLK_DIR */
+#define WM8903_BCLK_DIR_WIDTH 1 /* BCLK_DIR */
+#define WM8903_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */
+#define WM8903_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */
+#define WM8903_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */
+#define WM8903_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */
+#define WM8903_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */
+#define WM8903_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */
+#define WM8903_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */
+
+/*
+ * R26 (0x1A) - Audio Interface 2
+ */
+#define WM8903_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */
+#define WM8903_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */
+#define WM8903_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */
+
+/*
+ * R27 (0x1B) - Audio Interface 3
+ */
+#define WM8903_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */
+#define WM8903_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */
+#define WM8903_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */
+
+/*
+ * R30 (0x1E) - DAC Digital Volume Left
+ */
+#define WM8903_DACVU 0x0100 /* DACVU */
+#define WM8903_DACVU_MASK 0x0100 /* DACVU */
+#define WM8903_DACVU_SHIFT 8 /* DACVU */
+#define WM8903_DACVU_WIDTH 1 /* DACVU */
+#define WM8903_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8903_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */
+#define WM8903_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital Volume Right
+ */
+#define WM8903_DACVU 0x0100 /* DACVU */
+#define WM8903_DACVU_MASK 0x0100 /* DACVU */
+#define WM8903_DACVU_SHIFT 8 /* DACVU */
+#define WM8903_DACVU_WIDTH 1 /* DACVU */
+#define WM8903_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8903_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */
+#define WM8903_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */
+
+/*
+ * R32 (0x20) - DAC Digital 0
+ */
+#define WM8903_ADCL_DAC_SVOL_MASK 0x0F00 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCL_DAC_SVOL_SHIFT 8 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */
+#define WM8903_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */
+#define WM8903_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R33 (0x21) - DAC Digital 1
+ */
+#define WM8903_DAC_MONO 0x1000 /* DAC_MONO */
+#define WM8903_DAC_MONO_MASK 0x1000 /* DAC_MONO */
+#define WM8903_DAC_MONO_SHIFT 12 /* DAC_MONO */
+#define WM8903_DAC_MONO_WIDTH 1 /* DAC_MONO */
+#define WM8903_DAC_SB_FILT 0x0800 /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_MASK 0x0800 /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_SHIFT 11 /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */
+#define WM8903_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTEMODE 0x0200 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_MASK 0x0200 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_SHIFT 9 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTE 0x0008 /* DAC_MUTE */
+#define WM8903_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */
+#define WM8903_DAC_MUTE_SHIFT 3 /* DAC_MUTE */
+#define WM8903_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM8903_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM8903_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM8903_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+
+/*
+ * R36 (0x24) - ADC Digital Volume Left
+ */
+#define WM8903_ADCVU 0x0100 /* ADCVU */
+#define WM8903_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8903_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8903_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8903_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8903_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */
+#define WM8903_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */
+
+/*
+ * R37 (0x25) - ADC Digital Volume Right
+ */
+#define WM8903_ADCVU 0x0100 /* ADCVU */
+#define WM8903_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8903_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8903_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8903_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8903_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */
+#define WM8903_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */
+
+/*
+ * R38 (0x26) - ADC Digital 0
+ */
+#define WM8903_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_ENA 0x0010 /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_MASK 0x0010 /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_SHIFT 4 /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_WIDTH 1 /* ADC_HPF_ENA */
+#define WM8903_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */
+#define WM8903_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */
+
+/*
+ * R39 (0x27) - Digital Microphone 0
+ */
+#define WM8903_DIGMIC_MODE_SEL 0x0100 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_MASK 0x0100 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_SHIFT 8 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_WIDTH 1 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_CLK_SEL_L_MASK 0x00C0 /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_L_SHIFT 6 /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_L_WIDTH 2 /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_R_MASK 0x0030 /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_R_SHIFT 4 /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_R_WIDTH 2 /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_RT_MASK 0x000C /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_RT_SHIFT 2 /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_RT_WIDTH 2 /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_MASK 0x0003 /* DIGMIC_CLK_SEL - [1:0] */
+#define WM8903_DIGMIC_CLK_SEL_SHIFT 0 /* DIGMIC_CLK_SEL - [1:0] */
+#define WM8903_DIGMIC_CLK_SEL_WIDTH 2 /* DIGMIC_CLK_SEL - [1:0] */
+
+/*
+ * R40 (0x28) - DRC 0
+ */
+#define WM8903_DRC_ENA 0x8000 /* DRC_ENA */
+#define WM8903_DRC_ENA_MASK 0x8000 /* DRC_ENA */
+#define WM8903_DRC_ENA_SHIFT 15 /* DRC_ENA */
+#define WM8903_DRC_ENA_WIDTH 1 /* DRC_ENA */
+#define WM8903_DRC_THRESH_HYST_MASK 0x1800 /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_THRESH_HYST_SHIFT 11 /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_FF_DELAY 0x0020 /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_MASK 0x0020 /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_SHIFT 5 /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */
+#define WM8903_DRC_SMOOTH_ENA 0x0008 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_MASK 0x0008 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_SHIFT 3 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_QR_ENA 0x0004 /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_MASK 0x0004 /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_SHIFT 2 /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */
+#define WM8903_DRC_ANTICLIP_ENA 0x0002 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_MASK 0x0002 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_SHIFT 1 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_HYST_ENA 0x0001 /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_MASK 0x0001 /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_SHIFT 0 /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */
+
+/*
+ * R41 (0x29) - DRC 1
+ */
+#define WM8903_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_THRESH_QR_MASK 0x00C0 /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_THRESH_QR_SHIFT 6 /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_RATE_QR_MASK 0x0030 /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_RATE_QR_SHIFT 4 /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM8903_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM8903_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R42 (0x2A) - DRC 2
+ */
+#define WM8903_DRC_R0_SLOPE_COMP_MASK 0x0038 /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R0_SLOPE_COMP_SHIFT 3 /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R1_SLOPE_COMP_MASK 0x0007 /* DRC_R1_SLOPE_COMP - [2:0] */
+#define WM8903_DRC_R1_SLOPE_COMP_SHIFT 0 /* DRC_R1_SLOPE_COMP - [2:0] */
+#define WM8903_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [2:0] */
+
+/*
+ * R43 (0x2B) - DRC 3
+ */
+#define WM8903_DRC_THRESH_COMP_MASK 0x07E0 /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_THRESH_COMP_SHIFT 5 /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_AMP_COMP_MASK 0x001F /* DRC_AMP_COMP - [4:0] */
+#define WM8903_DRC_AMP_COMP_SHIFT 0 /* DRC_AMP_COMP - [4:0] */
+#define WM8903_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [4:0] */
+
+/*
+ * R44 (0x2C) - Analogue Left Input 0
+ */
+#define WM8903_LINMUTE 0x0080 /* LINMUTE */
+#define WM8903_LINMUTE_MASK 0x0080 /* LINMUTE */
+#define WM8903_LINMUTE_SHIFT 7 /* LINMUTE */
+#define WM8903_LINMUTE_WIDTH 1 /* LINMUTE */
+#define WM8903_LIN_VOL_MASK 0x001F /* LIN_VOL - [4:0] */
+#define WM8903_LIN_VOL_SHIFT 0 /* LIN_VOL - [4:0] */
+#define WM8903_LIN_VOL_WIDTH 5 /* LIN_VOL - [4:0] */
+
+/*
+ * R45 (0x2D) - Analogue Right Input 0
+ */
+#define WM8903_RINMUTE 0x0080 /* RINMUTE */
+#define WM8903_RINMUTE_MASK 0x0080 /* RINMUTE */
+#define WM8903_RINMUTE_SHIFT 7 /* RINMUTE */
+#define WM8903_RINMUTE_WIDTH 1 /* RINMUTE */
+#define WM8903_RIN_VOL_MASK 0x001F /* RIN_VOL - [4:0] */
+#define WM8903_RIN_VOL_SHIFT 0 /* RIN_VOL - [4:0] */
+#define WM8903_RIN_VOL_WIDTH 5 /* RIN_VOL - [4:0] */
+
+/*
+ * R46 (0x2E) - Analogue Left Input 1
+ */
+#define WM8903_INL_CM_ENA 0x0040 /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_MASK 0x0040 /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_SHIFT 6 /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_WIDTH 1 /* INL_CM_ENA */
+#define WM8903_L_IP_SEL_N_MASK 0x0030 /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_N_SHIFT 4 /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_N_WIDTH 2 /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_P_MASK 0x000C /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_IP_SEL_P_SHIFT 2 /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_IP_SEL_P_WIDTH 2 /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_MODE_MASK 0x0003 /* L_MODE - [1:0] */
+#define WM8903_L_MODE_SHIFT 0 /* L_MODE - [1:0] */
+#define WM8903_L_MODE_WIDTH 2 /* L_MODE - [1:0] */
+
+/*
+ * R47 (0x2F) - Analogue Right Input 1
+ */
+#define WM8903_INR_CM_ENA 0x0040 /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_MASK 0x0040 /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_SHIFT 6 /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_WIDTH 1 /* INR_CM_ENA */
+#define WM8903_R_IP_SEL_N_MASK 0x0030 /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_N_SHIFT 4 /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_N_WIDTH 2 /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_P_MASK 0x000C /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_IP_SEL_P_SHIFT 2 /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_IP_SEL_P_WIDTH 2 /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_MODE_MASK 0x0003 /* R_MODE - [1:0] */
+#define WM8903_R_MODE_SHIFT 0 /* R_MODE - [1:0] */
+#define WM8903_R_MODE_WIDTH 2 /* R_MODE - [1:0] */
+
+/*
+ * R50 (0x32) - Analogue Left Mix 0
+ */
+#define WM8903_DACL_TO_MIXOUTL 0x0008 /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_MASK 0x0008 /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_SHIFT 3 /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL 0x0004 /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_MASK 0x0004 /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_SHIFT 2 /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_WIDTH 1 /* DACR_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL 0x0002 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_MASK 0x0002 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_SHIFT 1 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_WIDTH 1 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL 0x0001 /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_MASK 0x0001 /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_SHIFT 0 /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_WIDTH 1 /* BYPASSR_TO_MIXOUTL */
+
+/*
+ * R51 (0x33) - Analogue Right Mix 0
+ */
+#define WM8903_DACL_TO_MIXOUTR 0x0008 /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_MASK 0x0008 /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_SHIFT 3 /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_WIDTH 1 /* DACL_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR 0x0004 /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_MASK 0x0004 /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_SHIFT 2 /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR 0x0002 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_MASK 0x0002 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_SHIFT 1 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_WIDTH 1 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR 0x0001 /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_MASK 0x0001 /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_SHIFT 0 /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_WIDTH 1 /* BYPASSR_TO_MIXOUTR */
+
+/*
+ * R52 (0x34) - Analogue Spk Mix Left 0
+ */
+#define WM8903_DACL_TO_MIXSPKL 0x0008 /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_MASK 0x0008 /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_SHIFT 3 /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_WIDTH 1 /* DACL_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL 0x0004 /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_MASK 0x0004 /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_SHIFT 2 /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_WIDTH 1 /* DACR_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL 0x0002 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_MASK 0x0002 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_SHIFT 1 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_WIDTH 1 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL 0x0001 /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_MASK 0x0001 /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_SHIFT 0 /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_WIDTH 1 /* BYPASSR_TO_MIXSPKL */
+
+/*
+ * R53 (0x35) - Analogue Spk Mix Left 1
+ */
+#define WM8903_DACL_MIXSPKL_VOL 0x0008 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_MASK 0x0008 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_SHIFT 3 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_WIDTH 1 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL 0x0004 /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_MASK 0x0004 /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_SHIFT 2 /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_WIDTH 1 /* DACR_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL 0x0002 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_MASK 0x0002 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_SHIFT 1 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_WIDTH 1 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL 0x0001 /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_MASK 0x0001 /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_SHIFT 0 /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_WIDTH 1 /* BYPASSR_MIXSPKL_VOL */
+
+/*
+ * R54 (0x36) - Analogue Spk Mix Right 0
+ */
+#define WM8903_DACL_TO_MIXSPKR 0x0008 /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_MASK 0x0008 /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_SHIFT 3 /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_WIDTH 1 /* DACL_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR 0x0004 /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_MASK 0x0004 /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_SHIFT 2 /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_WIDTH 1 /* DACR_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR 0x0002 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_MASK 0x0002 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_SHIFT 1 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_WIDTH 1 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR 0x0001 /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_MASK 0x0001 /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_SHIFT 0 /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_WIDTH 1 /* BYPASSR_TO_MIXSPKR */
+
+/*
+ * R55 (0x37) - Analogue Spk Mix Right 1
+ */
+#define WM8903_DACL_MIXSPKR_VOL 0x0008 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_MASK 0x0008 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_SHIFT 3 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_WIDTH 1 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL 0x0004 /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_MASK 0x0004 /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_SHIFT 2 /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_WIDTH 1 /* DACR_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL 0x0002 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_MASK 0x0002 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_SHIFT 1 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_WIDTH 1 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL 0x0001 /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_MASK 0x0001 /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_SHIFT 0 /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_WIDTH 1 /* BYPASSR_MIXSPKR_VOL */
+
+/*
+ * R57 (0x39) - Analogue OUT1 Left
+ */
+#define WM8903_HPL_MUTE 0x0100 /* HPL_MUTE */
+#define WM8903_HPL_MUTE_MASK 0x0100 /* HPL_MUTE */
+#define WM8903_HPL_MUTE_SHIFT 8 /* HPL_MUTE */
+#define WM8903_HPL_MUTE_WIDTH 1 /* HPL_MUTE */
+#define WM8903_HPOUTVU 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */
+#define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */
+#define WM8903_HPOUTLZC 0x0040 /* HPOUTLZC */
+#define WM8903_HPOUTLZC_MASK 0x0040 /* HPOUTLZC */
+#define WM8903_HPOUTLZC_SHIFT 6 /* HPOUTLZC */
+#define WM8903_HPOUTLZC_WIDTH 1 /* HPOUTLZC */
+#define WM8903_HPOUTL_VOL_MASK 0x003F /* HPOUTL_VOL - [5:0] */
+#define WM8903_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [5:0] */
+#define WM8903_HPOUTL_VOL_WIDTH 6 /* HPOUTL_VOL - [5:0] */
+
+/*
+ * R58 (0x3A) - Analogue OUT1 Right
+ */
+#define WM8903_HPR_MUTE 0x0100 /* HPR_MUTE */
+#define WM8903_HPR_MUTE_MASK 0x0100 /* HPR_MUTE */
+#define WM8903_HPR_MUTE_SHIFT 8 /* HPR_MUTE */
+#define WM8903_HPR_MUTE_WIDTH 1 /* HPR_MUTE */
+#define WM8903_HPOUTVU 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */
+#define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */
+#define WM8903_HPOUTRZC 0x0040 /* HPOUTRZC */
+#define WM8903_HPOUTRZC_MASK 0x0040 /* HPOUTRZC */
+#define WM8903_HPOUTRZC_SHIFT 6 /* HPOUTRZC */
+#define WM8903_HPOUTRZC_WIDTH 1 /* HPOUTRZC */
+#define WM8903_HPOUTR_VOL_MASK 0x003F /* HPOUTR_VOL - [5:0] */
+#define WM8903_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [5:0] */
+#define WM8903_HPOUTR_VOL_WIDTH 6 /* HPOUTR_VOL - [5:0] */
+
+/*
+ * R59 (0x3B) - Analogue OUT2 Left
+ */
+#define WM8903_LINEOUTL_MUTE 0x0100 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_MASK 0x0100 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_SHIFT 8 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_WIDTH 1 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */
+#define WM8903_LINEOUTLZC 0x0040 /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_MASK 0x0040 /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_SHIFT 6 /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_WIDTH 1 /* LINEOUTLZC */
+#define WM8903_LINEOUTL_VOL_MASK 0x003F /* LINEOUTL_VOL - [5:0] */
+#define WM8903_LINEOUTL_VOL_SHIFT 0 /* LINEOUTL_VOL - [5:0] */
+#define WM8903_LINEOUTL_VOL_WIDTH 6 /* LINEOUTL_VOL - [5:0] */
+
+/*
+ * R60 (0x3C) - Analogue OUT2 Right
+ */
+#define WM8903_LINEOUTR_MUTE 0x0100 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_MASK 0x0100 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_SHIFT 8 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_WIDTH 1 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */
+#define WM8903_LINEOUTRZC 0x0040 /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_MASK 0x0040 /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_SHIFT 6 /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_WIDTH 1 /* LINEOUTRZC */
+#define WM8903_LINEOUTR_VOL_MASK 0x003F /* LINEOUTR_VOL - [5:0] */
+#define WM8903_LINEOUTR_VOL_SHIFT 0 /* LINEOUTR_VOL - [5:0] */
+#define WM8903_LINEOUTR_VOL_WIDTH 6 /* LINEOUTR_VOL - [5:0] */
+
+/*
+ * R62 (0x3E) - Analogue OUT3 Left
+ */
+#define WM8903_SPKL_MUTE 0x0100 /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_MASK 0x0100 /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_SHIFT 8 /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8903_SPKVU 0x0080 /* SPKVU */
+#define WM8903_SPKVU_MASK 0x0080 /* SPKVU */
+#define WM8903_SPKVU_SHIFT 7 /* SPKVU */
+#define WM8903_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8903_SPKLZC 0x0040 /* SPKLZC */
+#define WM8903_SPKLZC_MASK 0x0040 /* SPKLZC */
+#define WM8903_SPKLZC_SHIFT 6 /* SPKLZC */
+#define WM8903_SPKLZC_WIDTH 1 /* SPKLZC */
+#define WM8903_SPKL_VOL_MASK 0x003F /* SPKL_VOL - [5:0] */
+#define WM8903_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [5:0] */
+#define WM8903_SPKL_VOL_WIDTH 6 /* SPKL_VOL - [5:0] */
+
+/*
+ * R63 (0x3F) - Analogue OUT3 Right
+ */
+#define WM8903_SPKR_MUTE 0x0100 /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_MASK 0x0100 /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_SHIFT 8 /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8903_SPKVU 0x0080 /* SPKVU */
+#define WM8903_SPKVU_MASK 0x0080 /* SPKVU */
+#define WM8903_SPKVU_SHIFT 7 /* SPKVU */
+#define WM8903_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8903_SPKRZC 0x0040 /* SPKRZC */
+#define WM8903_SPKRZC_MASK 0x0040 /* SPKRZC */
+#define WM8903_SPKRZC_SHIFT 6 /* SPKRZC */
+#define WM8903_SPKRZC_WIDTH 1 /* SPKRZC */
+#define WM8903_SPKR_VOL_MASK 0x003F /* SPKR_VOL - [5:0] */
+#define WM8903_SPKR_VOL_SHIFT 0 /* SPKR_VOL - [5:0] */
+#define WM8903_SPKR_VOL_WIDTH 6 /* SPKR_VOL - [5:0] */
+
+/*
+ * R65 (0x41) - Analogue SPK Output Control 0
+ */
+#define WM8903_SPK_DISCHARGE 0x0002 /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_MASK 0x0002 /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_SHIFT 1 /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_WIDTH 1 /* SPK_DISCHARGE */
+#define WM8903_VROI 0x0001 /* VROI */
+#define WM8903_VROI_MASK 0x0001 /* VROI */
+#define WM8903_VROI_SHIFT 0 /* VROI */
+#define WM8903_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R67 (0x43) - DC Servo 0
+ */
+#define WM8903_DCS_MASTER_ENA 0x0010 /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_MASK 0x0010 /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_SHIFT 4 /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_WIDTH 1 /* DCS_MASTER_ENA */
+#define WM8903_DCS_ENA_MASK 0x000F /* DCS_ENA - [3:0] */
+#define WM8903_DCS_ENA_SHIFT 0 /* DCS_ENA - [3:0] */
+#define WM8903_DCS_ENA_WIDTH 4 /* DCS_ENA - [3:0] */
+
+/*
+ * R69 (0x45) - DC Servo 2
+ */
+#define WM8903_DCS_MODE_MASK 0x0003 /* DCS_MODE - [1:0] */
+#define WM8903_DCS_MODE_SHIFT 0 /* DCS_MODE - [1:0] */
+#define WM8903_DCS_MODE_WIDTH 2 /* DCS_MODE - [1:0] */
+
+/*
+ * R90 (0x5A) - Analogue HP 0
+ */
+#define WM8903_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */
+#define WM8903_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA 0x0010 /* HPL_ENA */
+#define WM8903_HPL_ENA_MASK 0x0010 /* HPL_ENA */
+#define WM8903_HPL_ENA_SHIFT 4 /* HPL_ENA */
+#define WM8903_HPL_ENA_WIDTH 1 /* HPL_ENA */
+#define WM8903_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */
+#define WM8903_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA 0x0001 /* HPR_ENA */
+#define WM8903_HPR_ENA_MASK 0x0001 /* HPR_ENA */
+#define WM8903_HPR_ENA_SHIFT 0 /* HPR_ENA */
+#define WM8903_HPR_ENA_WIDTH 1 /* HPR_ENA */
+
+/*
+ * R94 (0x5E) - Analogue Lineout 0
+ */
+#define WM8903_LINEOUTL_RMV_SHORT 0x0080 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_MASK 0x0080 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_SHIFT 7 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_WIDTH 1 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_ENA_OUTP 0x0040 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_MASK 0x0040 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_SHIFT 6 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_WIDTH 1 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_DLY 0x0020 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_MASK 0x0020 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_SHIFT 5 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_WIDTH 1 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA 0x0010 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_MASK 0x0010 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_SHIFT 4 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_WIDTH 1 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTR_RMV_SHORT 0x0008 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_MASK 0x0008 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_SHIFT 3 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_WIDTH 1 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_ENA_OUTP 0x0004 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_MASK 0x0004 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_SHIFT 2 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_WIDTH 1 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_DLY 0x0002 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_MASK 0x0002 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_SHIFT 1 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_WIDTH 1 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA 0x0001 /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_MASK 0x0001 /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_SHIFT 0 /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_WIDTH 1 /* LINEOUTR_ENA */
+
+/*
+ * R98 (0x62) - Charge Pump 0
+ */
+#define WM8903_CP_ENA 0x0001 /* CP_ENA */
+#define WM8903_CP_ENA_MASK 0x0001 /* CP_ENA */
+#define WM8903_CP_ENA_SHIFT 0 /* CP_ENA */
+#define WM8903_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R104 (0x68) - Class W 0
+ */
+#define WM8903_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_V 0x0001 /* CP_DYN_V */
+#define WM8903_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */
+#define WM8903_CP_DYN_V_SHIFT 0 /* CP_DYN_V */
+#define WM8903_CP_DYN_V_WIDTH 1 /* CP_DYN_V */
+
+/*
+ * R108 (0x6C) - Write Sequencer 0
+ */
+#define WM8903_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8903_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8903_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8903_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R109 (0x6D) - Write Sequencer 1
+ */
+#define WM8903_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8903_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8903_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R110 (0x6E) - Write Sequencer 2
+ */
+#define WM8903_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8903_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8903_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8903_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R111 (0x6F) - Write Sequencer 3
+ */
+#define WM8903_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8903_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8903_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8903_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8903_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8903_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8903_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8903_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R112 (0x70) - Write Sequencer 4
+ */
+#define WM8903_WSEQ_CURRENT_INDEX_MASK 0x03F0 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R114 (0x72) - Control Interface
+ */
+#define WM8903_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */
+
+/*
+ * R116 (0x74) - GPIO Control 1
+ */
+#define WM8903_GP1_FN_MASK 0x1F00 /* GP1_FN - [12:8] */
+#define WM8903_GP1_FN_SHIFT 8 /* GP1_FN - [12:8] */
+#define WM8903_GP1_FN_WIDTH 5 /* GP1_FN - [12:8] */
+#define WM8903_GP1_DIR 0x0080 /* GP1_DIR */
+#define WM8903_GP1_DIR_MASK 0x0080 /* GP1_DIR */
+#define WM8903_GP1_DIR_SHIFT 7 /* GP1_DIR */
+#define WM8903_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8903_GP1_OP_CFG 0x0040 /* GP1_OP_CFG */
+#define WM8903_GP1_OP_CFG_MASK 0x0040 /* GP1_OP_CFG */
+#define WM8903_GP1_OP_CFG_SHIFT 6 /* GP1_OP_CFG */
+#define WM8903_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8903_GP1_IP_CFG 0x0020 /* GP1_IP_CFG */
+#define WM8903_GP1_IP_CFG_MASK 0x0020 /* GP1_IP_CFG */
+#define WM8903_GP1_IP_CFG_SHIFT 5 /* GP1_IP_CFG */
+#define WM8903_GP1_IP_CFG_WIDTH 1 /* GP1_IP_CFG */
+#define WM8903_GP1_LVL 0x0010 /* GP1_LVL */
+#define WM8903_GP1_LVL_MASK 0x0010 /* GP1_LVL */
+#define WM8903_GP1_LVL_SHIFT 4 /* GP1_LVL */
+#define WM8903_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8903_GP1_PD 0x0008 /* GP1_PD */
+#define WM8903_GP1_PD_MASK 0x0008 /* GP1_PD */
+#define WM8903_GP1_PD_SHIFT 3 /* GP1_PD */
+#define WM8903_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8903_GP1_PU 0x0004 /* GP1_PU */
+#define WM8903_GP1_PU_MASK 0x0004 /* GP1_PU */
+#define WM8903_GP1_PU_SHIFT 2 /* GP1_PU */
+#define WM8903_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8903_GP1_INTMODE 0x0002 /* GP1_INTMODE */
+#define WM8903_GP1_INTMODE_MASK 0x0002 /* GP1_INTMODE */
+#define WM8903_GP1_INTMODE_SHIFT 1 /* GP1_INTMODE */
+#define WM8903_GP1_INTMODE_WIDTH 1 /* GP1_INTMODE */
+#define WM8903_GP1_DB 0x0001 /* GP1_DB */
+#define WM8903_GP1_DB_MASK 0x0001 /* GP1_DB */
+#define WM8903_GP1_DB_SHIFT 0 /* GP1_DB */
+#define WM8903_GP1_DB_WIDTH 1 /* GP1_DB */
+
+/*
+ * R117 (0x75) - GPIO Control 2
+ */
+#define WM8903_GP2_FN_MASK 0x1F00 /* GP2_FN - [12:8] */
+#define WM8903_GP2_FN_SHIFT 8 /* GP2_FN - [12:8] */
+#define WM8903_GP2_FN_WIDTH 5 /* GP2_FN - [12:8] */
+#define WM8903_GP2_DIR 0x0080 /* GP2_DIR */
+#define WM8903_GP2_DIR_MASK 0x0080 /* GP2_DIR */
+#define WM8903_GP2_DIR_SHIFT 7 /* GP2_DIR */
+#define WM8903_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8903_GP2_OP_CFG 0x0040 /* GP2_OP_CFG */
+#define WM8903_GP2_OP_CFG_MASK 0x0040 /* GP2_OP_CFG */
+#define WM8903_GP2_OP_CFG_SHIFT 6 /* GP2_OP_CFG */
+#define WM8903_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8903_GP2_IP_CFG 0x0020 /* GP2_IP_CFG */
+#define WM8903_GP2_IP_CFG_MASK 0x0020 /* GP2_IP_CFG */
+#define WM8903_GP2_IP_CFG_SHIFT 5 /* GP2_IP_CFG */
+#define WM8903_GP2_IP_CFG_WIDTH 1 /* GP2_IP_CFG */
+#define WM8903_GP2_LVL 0x0010 /* GP2_LVL */
+#define WM8903_GP2_LVL_MASK 0x0010 /* GP2_LVL */
+#define WM8903_GP2_LVL_SHIFT 4 /* GP2_LVL */
+#define WM8903_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8903_GP2_PD 0x0008 /* GP2_PD */
+#define WM8903_GP2_PD_MASK 0x0008 /* GP2_PD */
+#define WM8903_GP2_PD_SHIFT 3 /* GP2_PD */
+#define WM8903_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8903_GP2_PU 0x0004 /* GP2_PU */
+#define WM8903_GP2_PU_MASK 0x0004 /* GP2_PU */
+#define WM8903_GP2_PU_SHIFT 2 /* GP2_PU */
+#define WM8903_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8903_GP2_INTMODE 0x0002 /* GP2_INTMODE */
+#define WM8903_GP2_INTMODE_MASK 0x0002 /* GP2_INTMODE */
+#define WM8903_GP2_INTMODE_SHIFT 1 /* GP2_INTMODE */
+#define WM8903_GP2_INTMODE_WIDTH 1 /* GP2_INTMODE */
+#define WM8903_GP2_DB 0x0001 /* GP2_DB */
+#define WM8903_GP2_DB_MASK 0x0001 /* GP2_DB */
+#define WM8903_GP2_DB_SHIFT 0 /* GP2_DB */
+#define WM8903_GP2_DB_WIDTH 1 /* GP2_DB */
+
+/*
+ * R118 (0x76) - GPIO Control 3
+ */
+#define WM8903_GP3_FN_MASK 0x1F00 /* GP3_FN - [12:8] */
+#define WM8903_GP3_FN_SHIFT 8 /* GP3_FN - [12:8] */
+#define WM8903_GP3_FN_WIDTH 5 /* GP3_FN - [12:8] */
+#define WM8903_GP3_DIR 0x0080 /* GP3_DIR */
+#define WM8903_GP3_DIR_MASK 0x0080 /* GP3_DIR */
+#define WM8903_GP3_DIR_SHIFT 7 /* GP3_DIR */
+#define WM8903_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8903_GP3_OP_CFG 0x0040 /* GP3_OP_CFG */
+#define WM8903_GP3_OP_CFG_MASK 0x0040 /* GP3_OP_CFG */
+#define WM8903_GP3_OP_CFG_SHIFT 6 /* GP3_OP_CFG */
+#define WM8903_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8903_GP3_IP_CFG 0x0020 /* GP3_IP_CFG */
+#define WM8903_GP3_IP_CFG_MASK 0x0020 /* GP3_IP_CFG */
+#define WM8903_GP3_IP_CFG_SHIFT 5 /* GP3_IP_CFG */
+#define WM8903_GP3_IP_CFG_WIDTH 1 /* GP3_IP_CFG */
+#define WM8903_GP3_LVL 0x0010 /* GP3_LVL */
+#define WM8903_GP3_LVL_MASK 0x0010 /* GP3_LVL */
+#define WM8903_GP3_LVL_SHIFT 4 /* GP3_LVL */
+#define WM8903_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8903_GP3_PD 0x0008 /* GP3_PD */
+#define WM8903_GP3_PD_MASK 0x0008 /* GP3_PD */
+#define WM8903_GP3_PD_SHIFT 3 /* GP3_PD */
+#define WM8903_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8903_GP3_PU 0x0004 /* GP3_PU */
+#define WM8903_GP3_PU_MASK 0x0004 /* GP3_PU */
+#define WM8903_GP3_PU_SHIFT 2 /* GP3_PU */
+#define WM8903_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8903_GP3_INTMODE 0x0002 /* GP3_INTMODE */
+#define WM8903_GP3_INTMODE_MASK 0x0002 /* GP3_INTMODE */
+#define WM8903_GP3_INTMODE_SHIFT 1 /* GP3_INTMODE */
+#define WM8903_GP3_INTMODE_WIDTH 1 /* GP3_INTMODE */
+#define WM8903_GP3_DB 0x0001 /* GP3_DB */
+#define WM8903_GP3_DB_MASK 0x0001 /* GP3_DB */
+#define WM8903_GP3_DB_SHIFT 0 /* GP3_DB */
+#define WM8903_GP3_DB_WIDTH 1 /* GP3_DB */
+
+/*
+ * R119 (0x77) - GPIO Control 4
+ */
+#define WM8903_GP4_FN_MASK 0x1F00 /* GP4_FN - [12:8] */
+#define WM8903_GP4_FN_SHIFT 8 /* GP4_FN - [12:8] */
+#define WM8903_GP4_FN_WIDTH 5 /* GP4_FN - [12:8] */
+#define WM8903_GP4_DIR 0x0080 /* GP4_DIR */
+#define WM8903_GP4_DIR_MASK 0x0080 /* GP4_DIR */
+#define WM8903_GP4_DIR_SHIFT 7 /* GP4_DIR */
+#define WM8903_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8903_GP4_OP_CFG 0x0040 /* GP4_OP_CFG */
+#define WM8903_GP4_OP_CFG_MASK 0x0040 /* GP4_OP_CFG */
+#define WM8903_GP4_OP_CFG_SHIFT 6 /* GP4_OP_CFG */
+#define WM8903_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8903_GP4_IP_CFG 0x0020 /* GP4_IP_CFG */
+#define WM8903_GP4_IP_CFG_MASK 0x0020 /* GP4_IP_CFG */
+#define WM8903_GP4_IP_CFG_SHIFT 5 /* GP4_IP_CFG */
+#define WM8903_GP4_IP_CFG_WIDTH 1 /* GP4_IP_CFG */
+#define WM8903_GP4_LVL 0x0010 /* GP4_LVL */
+#define WM8903_GP4_LVL_MASK 0x0010 /* GP4_LVL */
+#define WM8903_GP4_LVL_SHIFT 4 /* GP4_LVL */
+#define WM8903_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8903_GP4_PD 0x0008 /* GP4_PD */
+#define WM8903_GP4_PD_MASK 0x0008 /* GP4_PD */
+#define WM8903_GP4_PD_SHIFT 3 /* GP4_PD */
+#define WM8903_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8903_GP4_PU 0x0004 /* GP4_PU */
+#define WM8903_GP4_PU_MASK 0x0004 /* GP4_PU */
+#define WM8903_GP4_PU_SHIFT 2 /* GP4_PU */
+#define WM8903_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8903_GP4_INTMODE 0x0002 /* GP4_INTMODE */
+#define WM8903_GP4_INTMODE_MASK 0x0002 /* GP4_INTMODE */
+#define WM8903_GP4_INTMODE_SHIFT 1 /* GP4_INTMODE */
+#define WM8903_GP4_INTMODE_WIDTH 1 /* GP4_INTMODE */
+#define WM8903_GP4_DB 0x0001 /* GP4_DB */
+#define WM8903_GP4_DB_MASK 0x0001 /* GP4_DB */
+#define WM8903_GP4_DB_SHIFT 0 /* GP4_DB */
+#define WM8903_GP4_DB_WIDTH 1 /* GP4_DB */
+
+/*
+ * R120 (0x78) - GPIO Control 5
+ */
+#define WM8903_GP5_FN_MASK 0x1F00 /* GP5_FN - [12:8] */
+#define WM8903_GP5_FN_SHIFT 8 /* GP5_FN - [12:8] */
+#define WM8903_GP5_FN_WIDTH 5 /* GP5_FN - [12:8] */
+#define WM8903_GP5_DIR 0x0080 /* GP5_DIR */
+#define WM8903_GP5_DIR_MASK 0x0080 /* GP5_DIR */
+#define WM8903_GP5_DIR_SHIFT 7 /* GP5_DIR */
+#define WM8903_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8903_GP5_OP_CFG 0x0040 /* GP5_OP_CFG */
+#define WM8903_GP5_OP_CFG_MASK 0x0040 /* GP5_OP_CFG */
+#define WM8903_GP5_OP_CFG_SHIFT 6 /* GP5_OP_CFG */
+#define WM8903_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8903_GP5_IP_CFG 0x0020 /* GP5_IP_CFG */
+#define WM8903_GP5_IP_CFG_MASK 0x0020 /* GP5_IP_CFG */
+#define WM8903_GP5_IP_CFG_SHIFT 5 /* GP5_IP_CFG */
+#define WM8903_GP5_IP_CFG_WIDTH 1 /* GP5_IP_CFG */
+#define WM8903_GP5_LVL 0x0010 /* GP5_LVL */
+#define WM8903_GP5_LVL_MASK 0x0010 /* GP5_LVL */
+#define WM8903_GP5_LVL_SHIFT 4 /* GP5_LVL */
+#define WM8903_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8903_GP5_PD 0x0008 /* GP5_PD */
+#define WM8903_GP5_PD_MASK 0x0008 /* GP5_PD */
+#define WM8903_GP5_PD_SHIFT 3 /* GP5_PD */
+#define WM8903_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8903_GP5_PU 0x0004 /* GP5_PU */
+#define WM8903_GP5_PU_MASK 0x0004 /* GP5_PU */
+#define WM8903_GP5_PU_SHIFT 2 /* GP5_PU */
+#define WM8903_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8903_GP5_INTMODE 0x0002 /* GP5_INTMODE */
+#define WM8903_GP5_INTMODE_MASK 0x0002 /* GP5_INTMODE */
+#define WM8903_GP5_INTMODE_SHIFT 1 /* GP5_INTMODE */
+#define WM8903_GP5_INTMODE_WIDTH 1 /* GP5_INTMODE */
+#define WM8903_GP5_DB 0x0001 /* GP5_DB */
+#define WM8903_GP5_DB_MASK 0x0001 /* GP5_DB */
+#define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */
+#define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */
+
+/*
+ * R121 (0x79) - Interrupt Status 1
+ */
+#define WM8903_MICSHRT_EINT 0x8000 /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_MASK 0x8000 /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_SHIFT 15 /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_WIDTH 1 /* MICSHRT_EINT */
+#define WM8903_MICDET_EINT 0x4000 /* MICDET_EINT */
+#define WM8903_MICDET_EINT_MASK 0x4000 /* MICDET_EINT */
+#define WM8903_MICDET_EINT_SHIFT 14 /* MICDET_EINT */
+#define WM8903_MICDET_EINT_WIDTH 1 /* MICDET_EINT */
+#define WM8903_WSEQ_BUSY_EINT 0x2000 /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_MASK 0x2000 /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_SHIFT 13 /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */
+#define WM8903_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8903_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8903_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8903_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8903_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8903_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8903_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8903_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8903_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8903_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8903_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8903_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8903_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8903_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8903_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8903_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8903_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8903_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8903_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8903_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R122 (0x7A) - Interrupt Status 1 Mask
+ */
+#define WM8903_IM_MICSHRT_EINT 0x8000 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_MASK 0x8000 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_SHIFT 15 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_WIDTH 1 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICDET_EINT 0x4000 /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_MASK 0x4000 /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_SHIFT 14 /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_WIDTH 1 /* IM_MICDET_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT 0x2000 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_MASK 0x2000 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_SHIFT 13 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8903_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8903_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8903_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8903_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R123 (0x7B) - Interrupt Polarity 1
+ */
+#define WM8903_MICSHRT_INV 0x8000 /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_MASK 0x8000 /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_SHIFT 15 /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_WIDTH 1 /* MICSHRT_INV */
+#define WM8903_MICDET_INV 0x4000 /* MICDET_INV */
+#define WM8903_MICDET_INV_MASK 0x4000 /* MICDET_INV */
+#define WM8903_MICDET_INV_SHIFT 14 /* MICDET_INV */
+#define WM8903_MICDET_INV_WIDTH 1 /* MICDET_INV */
+
+/*
+ * R126 (0x7E) - Interrupt Control
+ */
+#define WM8903_IRQ_POL 0x0001 /* IRQ_POL */
+#define WM8903_IRQ_POL_MASK 0x0001 /* IRQ_POL */
+#define WM8903_IRQ_POL_SHIFT 0 /* IRQ_POL */
+#define WM8903_IRQ_POL_WIDTH 1 /* IRQ_POL */
+
+/*
+ * R129 (0x81) - Control Interface Test 1
+ */
+#define WM8903_USER_KEY 0x0002 /* USER_KEY */
+#define WM8903_USER_KEY_MASK 0x0002 /* USER_KEY */
+#define WM8903_USER_KEY_SHIFT 1 /* USER_KEY */
+#define WM8903_USER_KEY_WIDTH 1 /* USER_KEY */
+#define WM8903_TEST_KEY 0x0001 /* TEST_KEY */
+#define WM8903_TEST_KEY_MASK 0x0001 /* TEST_KEY */
+#define WM8903_TEST_KEY_SHIFT 0 /* TEST_KEY */
+#define WM8903_TEST_KEY_WIDTH 1 /* TEST_KEY */
+
+/*
+ * R149 (0x95) - Charge Pump Test 1
+ */
+#define WM8903_CP_SW_KELVIN_MODE_MASK 0x0006 /* CP_SW_KELVIN_MODE - [2:1] */
+#define WM8903_CP_SW_KELVIN_MODE_SHIFT 1 /* CP_SW_KELVIN_MODE - [2:1] */
+#define WM8903_CP_SW_KELVIN_MODE_WIDTH 2 /* CP_SW_KELVIN_MODE - [2:1] */
+
+/*
+ * R164 (0xA4) - Clock Rate Test 4
+ */
+#define WM8903_ADC_DIG_MIC 0x0200 /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_MASK 0x0200 /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_SHIFT 9 /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_WIDTH 1 /* ADC_DIG_MIC */
+
+/*
+ * R172 (0xAC) - Analogue Output Bias 0
+ */
+#define WM8903_PGA_BIAS_MASK 0x0070 /* PGA_BIAS - [6:4] */
+#define WM8903_PGA_BIAS_SHIFT 4 /* PGA_BIAS - [6:4] */
+#define WM8903_PGA_BIAS_WIDTH 3 /* PGA_BIAS - [6:4] */
+
+#endif
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
new file mode 100644
index 00000000000..f41a578ddd4
--- /dev/null
+++ b/sound/soc/codecs/wm8971.c
@@ -0,0 +1,941 @@
+/*
+ * wm8971.c -- WM8971 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/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 "wm8971.h"
+
+#define WM8971_VERSION "0.9"
+
+#define WM8971_REG_COUNT 43
+
+static struct workqueue_struct *wm8971_workq = NULL;
+
+/* codec private data */
+struct wm8971_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * wm8971 register cache
+ * We can't read the WM8971 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8971_reg[] = {
+ 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
+ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
+ 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
+ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
+ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
+ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
+ 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
+ 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
+ 0x0079, 0x0079, 0x0079, /* 40 */
+};
+
+static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg < WM8971_REG_COUNT)
+ return cache[reg];
+
+ return -1;
+}
+
+static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg < WM8971_REG_COUNT)
+ cache[reg] = value;
+}
+
+static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8753 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8971_write_reg_cache (codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0)
+
+/* WM8971 Controls */
+static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
+static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz",
+ "200Hz @ 48kHz" };
+static const char *wm8971_treble[] = { "8kHz", "4kHz" };
+static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
+static const char *wm8971_ng_type[] = { "Constant PGA Gain",
+ "Mute ADC Output" };
+static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
+static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA",
+ "Differential"};
+static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA",
+ "Differential"};
+static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
+static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
+static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+
+static const struct soc_enum wm8971_enum[] = {
+ SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */
+ SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
+ SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
+ SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
+ SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */
+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase),
+ SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */
+ SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
+ SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
+ SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */
+ SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
+};
+
+static const struct snd_kcontrol_new wm8971_snd_controls[] = {
+ SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0),
+ SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL,
+ 6, 1, 0),
+ SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1),
+
+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V,
+ WM8971_ROUT1V, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V,
+ WM8971_ROUT2V, 7, 1, 0),
+ SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
+
+ SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0),
+
+ SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1,
+ WM8971_LOUTM2, 4, 7, 1),
+ SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1,
+ WM8971_ROUTM2, 4, 7, 1),
+ SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1,
+ WM8971_MOUTM2, 4, 7, 1),
+
+ SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V,
+ WM8971_ROUT1V, 0, 127, 0),
+ SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V,
+ WM8971_ROUT2V, 0, 127, 0),
+
+ SOC_ENUM("Bass Boost", wm8971_enum[0]),
+ SOC_ENUM("Bass Filter", wm8971_enum[1]),
+ SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
+
+ SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
+ SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
+
+ SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
+
+ SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
+ SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
+
+ SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
+ SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
+ SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
+ SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
+ SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
+ SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
+ SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
+ SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
+
+ SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
+ SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
+
+ SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
+ SOC_ENUM("Playback Function", wm8971_enum[6]),
+ SOC_ENUM("Playback Phase", wm8971_enum[7]),
+
+ SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
+};
+
+/* add non-DAPM controls */
+static int wm8971_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8971_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const struct snd_kcontrol_new wm8971_left_line_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[8]);
+
+/* Right Line Mux */
+static const struct snd_kcontrol_new wm8971_right_line_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[9]);
+
+/* Left PGA Mux */
+static const struct snd_kcontrol_new wm8971_left_pga_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[10]);
+
+/* Right PGA Mux */
+static const struct snd_kcontrol_new wm8971_right_pga_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[11]);
+
+/* Mono ADC Mux */
+static const struct snd_kcontrol_new wm8971_monomux_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[13]);
+
+static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8971_left_mixer_controls[0],
+ ARRAY_SIZE(wm8971_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8971_right_mixer_controls[0],
+ ARRAY_SIZE(wm8971_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0,
+ &wm8971_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8971_mono_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0),
+ SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0,
+ &wm8971_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0,
+ &wm8971_right_pga_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_right_line_controls),
+
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_monomux_controls),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("MONO"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("MIC"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* left mixer */
+ {"Left Mixer", "Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* right mixer */
+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Right Mixer", "Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* left out 1 */
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+
+ /* left out 2 */
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT2", NULL, "Left Out 2"},
+
+ /* right out 1 */
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+
+ /* right out 2 */
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT2", NULL, "Right Out 2"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* mono out */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONO1", NULL, "Mono Out"},
+
+ /* Left Line Mux */
+ {"Left Line Mux", "Line", "LINPUT1"},
+ {"Left Line Mux", "PGA", "Left PGA Mux"},
+ {"Left Line Mux", "Differential", "Differential Mux"},
+
+ /* Right Line Mux */
+ {"Right Line Mux", "Line", "RINPUT1"},
+ {"Right Line Mux", "Mic", "MIC"},
+ {"Right Line Mux", "PGA", "Right PGA Mux"},
+ {"Right Line Mux", "Differential", "Differential Mux"},
+
+ /* Left PGA Mux */
+ {"Left PGA Mux", "Line", "LINPUT1"},
+ {"Left PGA Mux", "Differential", "Differential Mux"},
+
+ /* Right PGA Mux */
+ {"Right PGA Mux", "Line", "RINPUT1"},
+ {"Right PGA Mux", "Differential", "Differential Mux"},
+
+ /* Differential Mux */
+ {"Differential Mux", "Line", "LINPUT1"},
+ {"Differential Mux", "Line", "RINPUT1"},
+
+ /* Left ADC Mux */
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+ /* Right ADC Mux */
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+};
+
+static int wm8971_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8971_dapm_widgets,
+ ARRAY_SIZE(wm8971_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x6, 0x0},
+ {11289600, 8000, 1408, 0x16, 0x0},
+ {18432000, 8000, 2304, 0x7, 0x0},
+ {16934400, 8000, 2112, 0x17, 0x0},
+ {12000000, 8000, 1500, 0x6, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x18, 0x0},
+ {16934400, 11025, 1536, 0x19, 0x0},
+ {12000000, 11025, 1088, 0x19, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0xa, 0x0},
+ {18432000, 16000, 1152, 0xb, 0x0},
+ {12000000, 16000, 750, 0xa, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x1a, 0x0},
+ {16934400, 22050, 768, 0x1b, 0x0},
+ {12000000, 22050, 544, 0x1b, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0xc, 0x0},
+ {18432000, 32000, 576, 0xd, 0x0},
+ {12000000, 32000, 375, 0xa, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x10, 0x0},
+ {16934400, 44100, 384, 0x11, 0x0},
+ {12000000, 44100, 272, 0x11, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0},
+ {18432000, 48000, 384, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x1e, 0x0},
+ {16934400, 88200, 192, 0x1f, 0x0},
+ {12000000, 88200, 136, 0x1f, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0xe, 0x0},
+ {18432000, 96000, 192, 0xf, 0x0},
+ {12000000, 96000, 125, 0xe, 0x1},
+};
+
+static int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8971_priv *wm8971 = codec->private_data;
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8971->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8971_write(codec, WM8971_IFACE, iface);
+ return 0;
+}
+
+static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8971_priv *wm8971 = codec->private_data;
+ u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
+ u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
+ int coeff = get_coeff(wm8971->sysclk, params_rate(params));
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0008;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x000c;
+ break;
+ }
+
+ /* set iface & srate */
+ wm8971_write(codec, WM8971_IFACE, iface);
+ if (coeff >= 0)
+ wm8971_write(codec, WM8971_SRATE, srate |
+ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+ return 0;
+}
+
+static int wm8971_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
+
+ if (mute)
+ wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+ else
+ wm8971_write(codec, WM8971_ADCDAC, mute_reg);
+ return 0;
+}
+
+static int wm8971_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* set vmid to 50k and unmute dac */
+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* mute dac and set vmid to 500k, enable VREF */
+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+ break;
+ case SND_SOC_BIAS_OFF:
+ wm8971_write(codec, WM8971_PWR1, 0x0001);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8971_dai = {
+ .name = "WM8971",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8971_RATES,
+ .formats = WM8971_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8971_RATES,
+ .formats = WM8971_FORMATS,},
+ .ops = {
+ .hw_params = wm8971_pcm_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = wm8971_mute,
+ .set_fmt = wm8971_set_dai_fmt,
+ .set_sysclk = wm8971_set_dai_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8971_dai);
+
+static void wm8971_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec =
+ container_of(work, struct snd_soc_codec, delayed_work.work);
+ wm8971_set_bias_level(codec, codec->bias_level);
+}
+
+static int wm8971_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;
+
+ wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8971_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+ u16 reg;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) {
+ if (i + 1 == WM8971_RESET)
+ continue;
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+
+ wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* charge wm8971 caps */
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+ reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+ wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+ codec->bias_level = SND_SOC_BIAS_ON;
+ queue_delayed_work(wm8971_workq, &codec->delayed_work,
+ msecs_to_jiffies(1000));
+ }
+
+ return 0;
+}
+
+static int wm8971_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg, ret = 0;
+
+ codec->name = "WM8971";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8971_read_reg_cache;
+ codec->write = wm8971_write;
+ codec->set_bias_level = wm8971_set_bias_level;
+ codec->dai = &wm8971_dai;
+ codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
+ codec->num_dai = 1;
+ codec->reg_cache = kmemdup(wm8971_reg, sizeof(wm8971_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8971_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8971: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* charge output caps - set vmid to 5k for quick power up */
+ reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+ wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
+ queue_delayed_work(wm8971_workq, &codec->delayed_work,
+ msecs_to_jiffies(1000));
+
+ /* set the update bits */
+ reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
+ wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
+ reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
+ wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
+
+ reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
+ wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
+ reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
+ wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
+
+ reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
+ wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
+ reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
+ wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
+
+ reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
+ wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
+ reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
+ wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
+
+ wm8971_add_controls(codec);
+ wm8971_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8971: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+static struct snd_soc_device *wm8971_socdev;
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+
+static int wm8971_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = wm8971_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+
+ codec->control_data = i2c;
+
+ ret = wm8971_init(socdev);
+ if (ret < 0)
+ pr_err("failed to initialise WM8971\n");
+
+ return ret;
+}
+
+static int wm8971_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id wm8971_i2c_id[] = {
+ { "wm8971", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
+
+static struct i2c_driver wm8971_i2c_driver = {
+ .driver = {
+ .name = "WM8971 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8971_i2c_probe,
+ .remove = wm8971_i2c_remove,
+ .id_table = wm8971_i2c_id,
+};
+
+static int wm8971_add_i2c_device(struct platform_device *pdev,
+ const struct wm8971_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8971_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8971", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8971_i2c_driver);
+ return -ENODEV;
+}
+
+#endif
+
+static int wm8971_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8971_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct wm8971_priv *wm8971;
+ int ret = 0;
+
+ pr_info("WM8971 Audio Codec %s", WM8971_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL);
+ if (wm8971 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = wm8971;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ wm8971_socdev = socdev;
+
+ INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
+ wm8971_workq = create_workqueue("wm8971");
+ if (wm8971_workq == NULL) {
+ kfree(codec->private_data);
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = wm8971_add_i2c_device(pdev, setup);
+ }
+#endif
+ /* Add other interfaces here */
+
+ if (ret != 0) {
+ destroy_workqueue(wm8971_workq);
+ kfree(codec->private_data);
+ kfree(codec);
+ }
+
+ return ret;
+}
+
+/* power down chip */
+static int wm8971_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ if (wm8971_workq)
+ destroy_workqueue(wm8971_workq);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&wm8971_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8971 = {
+ .probe = wm8971_probe,
+ .remove = wm8971_remove,
+ .suspend = wm8971_suspend,
+ .resume = wm8971_resume,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
+
+MODULE_DESCRIPTION("ASoC WM8971 driver");
+MODULE_AUTHOR("Lab126");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h
new file mode 100644
index 00000000000..ef4f08f9f34
--- /dev/null
+++ b/sound/soc/codecs/wm8971.h
@@ -0,0 +1,64 @@
+/*
+ * wm8971.h -- audio driver for WM8971
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _WM8971_H
+#define _WM8971_H
+
+#define WM8971_LINVOL 0x00
+#define WM8971_RINVOL 0x01
+#define WM8971_LOUT1V 0x02
+#define WM8971_ROUT1V 0x03
+#define WM8971_ADCDAC 0x05
+#define WM8971_IFACE 0x07
+#define WM8971_SRATE 0x08
+#define WM8971_LDAC 0x0a
+#define WM8971_RDAC 0x0b
+#define WM8971_BASS 0x0c
+#define WM8971_TREBLE 0x0d
+#define WM8971_RESET 0x0f
+#define WM8971_ALC1 0x11
+#define WM8971_ALC2 0x12
+#define WM8971_ALC3 0x13
+#define WM8971_NGATE 0x14
+#define WM8971_LADC 0x15
+#define WM8971_RADC 0x16
+#define WM8971_ADCTL1 0x17
+#define WM8971_ADCTL2 0x18
+#define WM8971_PWR1 0x19
+#define WM8971_PWR2 0x1a
+#define WM8971_ADCTL3 0x1b
+#define WM8971_ADCIN 0x1f
+#define WM8971_LADCIN 0x20
+#define WM8971_RADCIN 0x21
+#define WM8971_LOUTM1 0x22
+#define WM8971_LOUTM2 0x23
+#define WM8971_ROUTM1 0x24
+#define WM8971_ROUTM2 0x25
+#define WM8971_MOUTM1 0x26
+#define WM8971_MOUTM2 0x27
+#define WM8971_LOUT2V 0x28
+#define WM8971_ROUT2V 0x29
+#define WM8971_MOUTV 0x2A
+
+#define WM8971_SYSCLK 0
+
+struct wm8971_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8971_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8971;
+
+#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
new file mode 100644
index 00000000000..572d22b0880
--- /dev/null
+++ b/sound/soc/codecs/wm8990.c
@@ -0,0 +1,1635 @@
+/*
+ * wm8990.c -- WM8990 ALSA Soc Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * lg@opensource.wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.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 <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8990.h"
+
+#define WM8990_VERSION "0.2"
+
+/* codec private data */
+struct wm8990_priv {
+ unsigned int sysclk;
+ unsigned int pcmclk;
+};
+
+/*
+ * wm8990 register cache. Note that register 0 is not included in the
+ * cache.
+ */
+static const u16 wm8990_reg[] = {
+ 0x8990, /* R0 - Reset */
+ 0x0000, /* R1 - Power Management (1) */
+ 0x6000, /* R2 - Power Management (2) */
+ 0x0000, /* R3 - Power Management (3) */
+ 0x4050, /* R4 - Audio Interface (1) */
+ 0x4000, /* R5 - Audio Interface (2) */
+ 0x01C8, /* R6 - Clocking (1) */
+ 0x0000, /* R7 - Clocking (2) */
+ 0x0040, /* R8 - Audio Interface (3) */
+ 0x0040, /* R9 - Audio Interface (4) */
+ 0x0004, /* R10 - DAC CTRL */
+ 0x00C0, /* R11 - Left DAC Digital Volume */
+ 0x00C0, /* R12 - Right DAC Digital Volume */
+ 0x0000, /* R13 - Digital Side Tone */
+ 0x0100, /* R14 - ADC CTRL */
+ 0x00C0, /* R15 - Left ADC Digital Volume */
+ 0x00C0, /* R16 - Right ADC Digital Volume */
+ 0x0000, /* R17 */
+ 0x0000, /* R18 - GPIO CTRL 1 */
+ 0x1000, /* R19 - GPIO1 & GPIO2 */
+ 0x1010, /* R20 - GPIO3 & GPIO4 */
+ 0x1010, /* R21 - GPIO5 & GPIO6 */
+ 0x8000, /* R22 - GPIOCTRL 2 */
+ 0x0800, /* R23 - GPIO_POL */
+ 0x008B, /* R24 - Left Line Input 1&2 Volume */
+ 0x008B, /* R25 - Left Line Input 3&4 Volume */
+ 0x008B, /* R26 - Right Line Input 1&2 Volume */
+ 0x008B, /* R27 - Right Line Input 3&4 Volume */
+ 0x0000, /* R28 - Left Output Volume */
+ 0x0000, /* R29 - Right Output Volume */
+ 0x0066, /* R30 - Line Outputs Volume */
+ 0x0022, /* R31 - Out3/4 Volume */
+ 0x0079, /* R32 - Left OPGA Volume */
+ 0x0079, /* R33 - Right OPGA Volume */
+ 0x0003, /* R34 - Speaker Volume */
+ 0x0003, /* R35 - ClassD1 */
+ 0x0000, /* R36 */
+ 0x0100, /* R37 - ClassD3 */
+ 0x0079, /* R38 - ClassD4 */
+ 0x0000, /* R39 - Input Mixer1 */
+ 0x0000, /* R40 - Input Mixer2 */
+ 0x0000, /* R41 - Input Mixer3 */
+ 0x0000, /* R42 - Input Mixer4 */
+ 0x0000, /* R43 - Input Mixer5 */
+ 0x0000, /* R44 - Input Mixer6 */
+ 0x0000, /* R45 - Output Mixer1 */
+ 0x0000, /* R46 - Output Mixer2 */
+ 0x0000, /* R47 - Output Mixer3 */
+ 0x0000, /* R48 - Output Mixer4 */
+ 0x0000, /* R49 - Output Mixer5 */
+ 0x0000, /* R50 - Output Mixer6 */
+ 0x0180, /* R51 - Out3/4 Mixer */
+ 0x0000, /* R52 - Line Mixer1 */
+ 0x0000, /* R53 - Line Mixer2 */
+ 0x0000, /* R54 - Speaker Mixer */
+ 0x0000, /* R55 - Additional Control */
+ 0x0000, /* R56 - AntiPOP1 */
+ 0x0000, /* R57 - AntiPOP2 */
+ 0x0000, /* R58 - MICBIAS */
+ 0x0000, /* R59 */
+ 0x0008, /* R60 - PLL1 */
+ 0x0031, /* R61 - PLL2 */
+ 0x0026, /* R62 - PLL3 */
+};
+
+/*
+ * read wm8990 register cache
+ */
+static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+ return cache[reg];
+}
+
+/*
+ * write wm8990 register cache
+ */
+static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+
+ /* Reset register is uncached */
+ if (reg == 0)
+ return;
+
+ cache[reg] = value;
+}
+
+/*
+ * write to the wm8990 register space
+ */
+static int wm8990_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ data[0] = reg & 0xFF;
+ data[1] = (value >> 8) & 0xFF;
+ data[2] = value & 0xFF;
+
+ wm8990_write_reg_cache(codec, reg, value);
+
+ if (codec->hw_write(codec->control_data, data, 3) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8990_reset(c) wm8990_write(c, WM8990_RESET, 0)
+
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+
+static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+
+static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+
+static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+
+static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+
+static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+
+static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+
+static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = wm8990_read_reg_cache(codec, reg);
+ return wm8990_write(codec, reg, val | 0x0100);
+}
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+ tlv_array) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+static const char *wm8990_digital_sidetone[] =
+ {"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8990_left_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADC_TO_DACL_SHIFT,
+ WM8990_ADC_TO_DACL_MASK,
+ wm8990_digital_sidetone);
+
+static const struct soc_enum wm8990_right_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADC_TO_DACR_SHIFT,
+ WM8990_ADC_TO_DACR_MASK,
+ wm8990_digital_sidetone);
+
+static const char *wm8990_adcmode[] =
+ {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8990_right_adcmode_enum =
+SOC_ENUM_SINGLE(WM8990_ADC_CTRL,
+ WM8990_ADC_HPF_CUT_SHIFT,
+ WM8990_ADC_HPF_CUT_MASK,
+ wm8990_adcmode);
+
+static const struct snd_kcontrol_new wm8990_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L12MNBST_BIT, 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L34MNBST_BIT, 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R12MNBST_BIT, 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R34MNBST_BIT, 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LLI3LOVOL_SHIFT, WM8990_LLI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LR12LOVOL_SHIFT, WM8990_LR12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LL12LOVOL_SHIFT, WM8990_LL12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRI3LOVOL_SHIFT, WM8990_LRI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RRI3ROVOL_SHIFT, WM8990_RRI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RL12ROVOL_SHIFT, WM8990_RL12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RR12ROVOL_SHIFT, WM8990_RR12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RLI3ROVOL_SHIFT, WM8990_RLI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RLBROVOL_SHIFT, WM8990_RLBROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RRBROVOL_SHIFT, WM8990_RRBROVOL_MASK, 1, out_mix_tlv),
+
+/* LOUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8990_LEFT_OUTPUT_VOLUME,
+ WM8990_LOUTVOL_SHIFT, WM8990_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8990_LEFT_OUTPUT_VOLUME, WM8990_LOZC_BIT, 1, 0),
+
+/* ROUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8990_RIGHT_OUTPUT_VOLUME,
+ WM8990_ROUTVOL_SHIFT, WM8990_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8990_RIGHT_OUTPUT_VOLUME, WM8990_ROZC_BIT, 1, 0),
+
+/* LOPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8990_LEFT_OPGA_VOLUME,
+ WM8990_LOPGAVOL_SHIFT, WM8990_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8990_LEFT_OPGA_VOLUME,
+ WM8990_LOPGAZC_BIT, 1, 0),
+
+/* ROPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8990_RIGHT_OPGA_VOLUME,
+ WM8990_ROPGAVOL_SHIFT, WM8990_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8990_RIGHT_OPGA_VOLUME,
+ WM8990_ROPGAZC_BIT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LONMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LOPMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LOATTN_BIT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_RONMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_ROPMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_ROATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT3MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT3ATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT4MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT4ATTN_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8990_CLASSD1,
+ WM8990_CDMODE_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8990_SPEAKER_VOLUME,
+ WM8990_SPKATTN_SHIFT, WM8990_SPKATTN_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8990_CLASSD3,
+ WM8990_DCGAIN_SHIFT, WM8990_DCGAIN_MASK, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8990_CLASSD3,
+ WM8990_ACGAIN_SHIFT, WM8990_ACGAIN_MASK, 0),
+SOC_SINGLE_TLV("Speaker Volume", WM8990_CLASSD4,
+ WM8990_SPKVOL_SHIFT, WM8990_SPKVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("Speaker ZC Switch", WM8990_CLASSD4,
+ WM8990_SPKZC_SHIFT, WM8990_SPKZC_MASK, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8990_LEFT_DAC_DIGITAL_VOLUME,
+ WM8990_DACL_VOL_SHIFT,
+ WM8990_DACL_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8990_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8990_DACR_VOL_SHIFT,
+ WM8990_DACR_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8990_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8990_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADCL_DAC_SVOL_SHIFT, WM8990_ADCL_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADCR_DAC_SVOL_SHIFT, WM8990_ADCR_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8990_ADC_CTRL,
+ WM8990_ADC_HPF_ENA_BIT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8990_right_adcmode_enum),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8990_LEFT_ADC_DIGITAL_VOLUME,
+ WM8990_ADCL_VOL_SHIFT,
+ WM8990_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8990_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8990_ADCR_VOL_SHIFT,
+ WM8990_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LIN12VOL_SHIFT,
+ WM8990_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LIN34VOL_SHIFT,
+ WM8990_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LI34MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RIN12VOL_SHIFT,
+ WM8990_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RIN34VOL_SHIFT,
+ WM8990_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RI34MUTE_BIT, 1, 0),
+
+};
+
+/* add non dapm controls */
+static int wm8990_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8990_snd_controls[i], codec,
+ NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u16 reg, fakepower;
+
+ reg = wm8990_read_reg_cache(w->codec, WM8990_POWER_MANAGEMENT_2);
+ fakepower = wm8990_read_reg_cache(w->codec, WM8990_INTDRIVBITS);
+
+ if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
+ (1 << WM8990_AINLMUX_PWR_BIT))) {
+ reg |= WM8990_AINL_ENA;
+ } else {
+ reg &= ~WM8990_AINL_ENA;
+ }
+
+ if (fakepower & ((1 << WM8990_INMIXR_PWR_BIT) |
+ (1 << WM8990_AINRMUX_PWR_BIT))) {
+ reg |= WM8990_AINR_ENA;
+ } else {
+ reg &= ~WM8990_AINL_ENA;
+ }
+ wm8990_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+ return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u32 reg_shift = kcontrol->private_value & 0xfff;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
+ reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER1);
+ if (reg & WM8990_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
+ reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER2);
+ if (reg & WM8990_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
+ reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+ if (reg & WM8990_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
+ reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+ if (reg & WM8990_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8990_INPUT_MIXER2, WM8990_LMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8990_INPUT_MIXER2, WM8990_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8990_INPUT_MIXER2, WM8990_LMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8990_INPUT_MIXER2, WM8990_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8990_INPUT_MIXER2, WM8990_RMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8990_INPUT_MIXER2, WM8990_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8990_INPUT_MIXER2, WM8990_RMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8990_INPUT_MIXER2, WM8990_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8990_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8990_INPUT_MIXER3,
+ WM8990_LDBVOL_SHIFT, WM8990_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8990_INPUT_MIXER5, WM8990_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+ 1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8990_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8990_INPUT_MIXER4,
+ WM8990_RDBVOL_SHIFT, WM8990_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8990_INPUT_MIXER6, WM8990_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+ 1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8990_ainlmux[] =
+ {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8990_ainlmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
+ ARRAY_SIZE(wm8990_ainlmux), wm8990_ainlmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8990_ainrmux[] =
+ {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8990_ainrmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
+ ARRAY_SIZE(wm8990_ainrmux), wm8990_ainrmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8990_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8990_INPUT_MIXER5, WM8990_LR4BVOL_SHIFT,
+ WM8990_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8990_INPUT_MIXER6, WM8990_RL4BVOL_SHIFT,
+ WM8990_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LRBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LLBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LRI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LLI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LR12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LL12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8990_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RLBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RRBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RLI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RRI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RL12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RR12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LLOPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LROPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8990_LINE_MIXER1,
+ WM8990_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER1,
+ WM8990_LR12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER1,
+ WM8990_LL12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RROPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RLOPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8990_LINE_MIXER2,
+ WM8990_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER2,
+ WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER2,
+ WM8990_RR12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+ WM8990_LI4O3_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8990_OUT3_4_MIXER,
+ WM8990_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8990_OUT3_4_MIXER,
+ WM8990_RPGAO4_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+ WM8990_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8990_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LI2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LB2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LOPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+ WM8990_ROPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2,
+ WM8990_ADCL_ENA_BIT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8990_POWER_MANAGEMENT_2,
+ WM8990_ADCR_ENA_BIT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN12_ENA_BIT,
+ 0, &wm8990_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN34_ENA_BIT,
+ 0, &wm8990_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN12_ENA_BIT,
+ 0, &wm8990_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT,
+ 0, &wm8990_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER_E("INMIXL", WM8990_INTDRIVBITS, WM8990_INMIXL_PWR_BIT, 0,
+ &wm8990_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8990_dapm_inmixl_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX_E("AILNMUX", WM8990_INTDRIVBITS, WM8990_AINLMUX_PWR_BIT, 0,
+ &wm8990_dapm_ainlmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER_E("INMIXR", WM8990_INTDRIVBITS, WM8990_INMIXR_PWR_BIT, 0,
+ &wm8990_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8990_dapm_inmixr_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX_E("AIRNMUX", WM8990_INTDRIVBITS, WM8990_AINRMUX_PWR_BIT, 0,
+ &wm8990_dapm_ainrmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8990_POWER_MANAGEMENT_3,
+ WM8990_DACL_ENA_BIT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8990_POWER_MANAGEMENT_3,
+ WM8990_DACR_ENA_BIT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOMIX_ENA_BIT,
+ 0, &wm8990_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LON_ENA_BIT, 0,
+ &wm8990_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOP_ENA_BIT, 0,
+ &wm8990_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT3_ENA_BIT, 0,
+ &wm8990_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8990_POWER_MANAGEMENT_1, WM8990_SPK_ENA_BIT, 0,
+ &wm8990_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT4_ENA_BIT, 0,
+ &wm8990_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROP_ENA_BIT, 0,
+ &wm8990_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_RON_ENA_BIT, 0,
+ &wm8990_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROMIX_ENA_BIT,
+ 0, &wm8990_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_LOUT_ENA_BIT, 0,
+ NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_ROUT_ENA_BIT, 0,
+ NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8990_POWER_MANAGEMENT_3, WM8990_LOPGA_ENA_BIT, 0,
+ NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8990_POWER_MANAGEMENT_3, WM8990_ROPGA_ENA_BIT, 0,
+ NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8990_POWER_MANAGEMENT_1,
+ WM8990_MICBIAS_ENA_BIT, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ /* Input Side */
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4"},
+ /* INMIXL */
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AILNMUX */
+ {"AILNMUX", "INMIXL Mix", "INMIXL"},
+ {"AILNMUX", "DIFFINL Mix", "LIN12PGA"},
+ {"AILNMUX", "DIFFINL Mix", "LIN34PGA"},
+ {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AILNMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4"},
+ /* INMIXL */
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AIRNMUX */
+ {"AIRNMUX", "INMIXR Mix", "INMIXR"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN12PGA"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN34PGA"},
+ {"AIRNMUX", "RXVOICE Mix", "RIN4/RXN"},
+ {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AIRNMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Left DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXP"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+static int wm8990_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8990_dapm_widgets,
+ ARRAY_SIZE(wm8990_dapm_widgets));
+
+ /* set up the WM8990 audio map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+ u32 div2;
+ u32 n;
+ u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->div2 = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8990 N value outwith recommended range! N = %d\n", Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ u16 reg;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct _pll_div pll_div;
+
+ if (freq_in && freq_out) {
+ pll_factors(&pll_div, freq_out * 4, freq_in);
+
+ /* Turn on PLL */
+ reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ reg |= WM8990_PLL_ENA;
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+ /* sysclk comes from PLL */
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2);
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
+
+ /* set up N , fractional mode and pre-divisor if neccessary */
+ wm8990_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+ (pll_div.div2?WM8990_PRESCALE:0));
+ wm8990_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+ wm8990_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+ } else {
+ /* Turn on PLL */
+ reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ reg &= ~WM8990_PLL_ENA;
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+ }
+ return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8990_priv *wm8990 = codec->private_data;
+
+ wm8990->sysclk = freq;
+ return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+ audio3 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8990_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8990_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8990_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8990_AIF_TMF_I2S;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8990_AIF_TMF_RIGHTJ;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8990_AIF_TMF_LEFTJ;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8990_AIF_TMF_DSP;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8990_AIF_TMF_DSP | WM8990_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8990_MCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ ~WM8990_MCLK_DIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ break;
+ case WM8990_DACCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ ~WM8990_DAC_CLKDIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ break;
+ case WM8990_ADCCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ ~WM8990_ADC_CLKDIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ break;
+ case WM8990_BCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_1) &
+ ~WM8990_BCLK_DIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8990_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;
+ u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8990_AIF_WL_MASK;
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ audio1 |= WM8990_AIF_WL_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ audio1 |= WM8990_AIF_WL_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio1 |= WM8990_AIF_WL_32BITS;
+ break;
+ }
+
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8990_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val;
+
+ val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+
+ if (mute)
+ wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+ else
+ wm8990_write(codec, WM8990_DAC_CTRL, val);
+
+ return 0;
+}
+
+static int wm8990_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 val;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Enable all output discharge bits */
+ wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+ WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+ WM8990_DIS_ROUT);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_VMIDTOG);
+
+ /* Delay to allow output caps to discharge */
+ msleep(msecs_to_jiffies(300));
+
+ /* Disable VMIDTOG */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL);
+
+ /* disable all output discharge bits */
+ wm8990_write(codec, WM8990_ANTIPOP1, 0);
+
+ /* Enable outputs */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+
+ msleep(msecs_to_jiffies(50));
+
+ /* Enable VMID at 2x50k */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+
+ msleep(msecs_to_jiffies(100));
+
+ /* Enable VREF */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+ msleep(msecs_to_jiffies(600));
+
+ /* Enable BUFIOEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_BUFIOEN);
+
+ /* Disable outputs */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+ } else {
+ /* ON -> standby */
+
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_POBCTRL | WM8990_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_BUFIOEN);
+
+ /* mute DAC */
+ val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
+ wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+ /* Disable VMID */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+
+ msleep(msecs_to_jiffies(300));
+
+ /* Enable all output discharge bits */
+ wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+ WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+ WM8990_DIS_ROUT);
+
+ /* Disable VREF */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, 0x0);
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8990_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM8990_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * The WM8990 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+struct snd_soc_dai wm8990_dai = {
+/* ADC/DAC on primary */
+ .name = "WM8990 ADC/DAC Primary",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8990_RATES,
+ .formats = WM8990_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8990_RATES,
+ .formats = WM8990_FORMATS,},
+ .ops = {
+ .hw_params = wm8990_hw_params,},
+ .dai_ops = {
+ .digital_mute = wm8990_mute,
+ .set_fmt = wm8990_set_dai_fmt,
+ .set_clkdiv = wm8990_set_dai_clkdiv,
+ .set_pll = wm8990_set_dai_pll,
+ .set_sysclk = wm8990_set_dai_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8990_dai);
+
+static int wm8990_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;
+
+ /* we only need to suspend if we are a valid card */
+ if (!codec->card)
+ return 0;
+
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8990_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* we only need to resume if we are a valid card */
+ if (!codec->card)
+ return 0;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
+ if (i + 1 == WM8990_RESET)
+ continue;
+ data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+/*
+ * initialise the WM8990 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8990_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 reg;
+ int ret = 0;
+
+ codec->name = "WM8990";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8990_read_reg_cache;
+ codec->write = wm8990_write;
+ codec->set_bias_level = wm8990_set_bias_level;
+ codec->dai = &wm8990_dai;
+ codec->num_dai = 2;
+ codec->reg_cache_size = ARRAY_SIZE(wm8990_reg);
+ codec->reg_cache = kmemdup(wm8990_reg, sizeof(wm8990_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8990_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8990: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* charge output caps */
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ reg = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_4);
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
+
+ reg = wm8990_read_reg_cache(codec, WM8990_GPIO1_GPIO2) &
+ ~WM8990_GPIO1_SEL_MASK;
+ wm8990_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
+
+ reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
+
+ wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+ wm8990_add_controls(codec);
+ wm8990_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8990: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+static struct snd_soc_device *wm8990_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM891 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x34
+ * high = 0x36
+ */
+
+static int wm8990_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = wm8990_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = wm8990_init(socdev);
+ if (ret < 0)
+ pr_err("failed to initialise WM8990\n");
+
+ return ret;
+}
+
+static int wm8990_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id wm8990_i2c_id[] = {
+ { "wm8990", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);
+
+static struct i2c_driver wm8990_i2c_driver = {
+ .driver = {
+ .name = "WM8990 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8990_i2c_probe,
+ .remove = wm8990_i2c_remove,
+ .id_table = wm8990_i2c_id,
+};
+
+static int wm8990_add_i2c_device(struct platform_device *pdev,
+ const struct wm8990_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8990_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8990", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8990_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+static int wm8990_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8990_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct wm8990_priv *wm8990;
+ int ret;
+
+ pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8990 = kzalloc(sizeof(struct wm8990_priv), GFP_KERNEL);
+ if (wm8990 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = wm8990;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ wm8990_socdev = socdev;
+
+ ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = wm8990_add_i2c_device(pdev, setup);
+ }
+#endif
+
+ if (ret != 0) {
+ kfree(codec->private_data);
+ kfree(codec);
+ }
+ return ret;
+}
+
+/* power down chip */
+static int wm8990_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&wm8990_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8990 = {
+ .probe = wm8990_probe,
+ .remove = wm8990_remove,
+ .suspend = wm8990_suspend,
+ .resume = wm8990_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
+
+MODULE_DESCRIPTION("ASoC WM8990 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
new file mode 100644
index 00000000000..0e192f3b078
--- /dev/null
+++ b/sound/soc/codecs/wm8990.h
@@ -0,0 +1,843 @@
+/*
+ * wm8990.h -- audio driver for WM8990
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __WM8990REGISTERDEFS_H__
+#define __WM8990REGISTERDEFS_H__
+
+/*
+ * Register values.
+ */
+#define WM8990_RESET 0x00
+#define WM8990_POWER_MANAGEMENT_1 0x01
+#define WM8990_POWER_MANAGEMENT_2 0x02
+#define WM8990_POWER_MANAGEMENT_3 0x03
+#define WM8990_AUDIO_INTERFACE_1 0x04
+#define WM8990_AUDIO_INTERFACE_2 0x05
+#define WM8990_CLOCKING_1 0x06
+#define WM8990_CLOCKING_2 0x07
+#define WM8990_AUDIO_INTERFACE_3 0x08
+#define WM8990_AUDIO_INTERFACE_4 0x09
+#define WM8990_DAC_CTRL 0x0A
+#define WM8990_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8990_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8990_DIGITAL_SIDE_TONE 0x0D
+#define WM8990_ADC_CTRL 0x0E
+#define WM8990_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8990_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8990_GPIO_CTRL_1 0x12
+#define WM8990_GPIO1_GPIO2 0x13
+#define WM8990_GPIO3_GPIO4 0x14
+#define WM8990_GPIO5_GPIO6 0x15
+#define WM8990_GPIOCTRL_2 0x16
+#define WM8990_GPIO_POL 0x17
+#define WM8990_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8990_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8990_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8990_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8990_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8990_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8990_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8990_OUT3_4_VOLUME 0x1F
+#define WM8990_LEFT_OPGA_VOLUME 0x20
+#define WM8990_RIGHT_OPGA_VOLUME 0x21
+#define WM8990_SPEAKER_VOLUME 0x22
+#define WM8990_CLASSD1 0x23
+#define WM8990_CLASSD3 0x25
+#define WM8990_CLASSD4 0x26
+#define WM8990_INPUT_MIXER1 0x27
+#define WM8990_INPUT_MIXER2 0x28
+#define WM8990_INPUT_MIXER3 0x29
+#define WM8990_INPUT_MIXER4 0x2A
+#define WM8990_INPUT_MIXER5 0x2B
+#define WM8990_INPUT_MIXER6 0x2C
+#define WM8990_OUTPUT_MIXER1 0x2D
+#define WM8990_OUTPUT_MIXER2 0x2E
+#define WM8990_OUTPUT_MIXER3 0x2F
+#define WM8990_OUTPUT_MIXER4 0x30
+#define WM8990_OUTPUT_MIXER5 0x31
+#define WM8990_OUTPUT_MIXER6 0x32
+#define WM8990_OUT3_4_MIXER 0x33
+#define WM8990_LINE_MIXER1 0x34
+#define WM8990_LINE_MIXER2 0x35
+#define WM8990_SPEAKER_MIXER 0x36
+#define WM8990_ADDITIONAL_CONTROL 0x37
+#define WM8990_ANTIPOP1 0x38
+#define WM8990_ANTIPOP2 0x39
+#define WM8990_MICBIAS 0x3A
+#define WM8990_PLL1 0x3C
+#define WM8990_PLL2 0x3D
+#define WM8990_PLL3 0x3E
+#define WM8990_INTDRIVBITS 0x3F
+
+#define WM8990_REGISTER_COUNT 60
+#define WM8990_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8990_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8990_SPK_ENA 0x1000 /* SPK_ENA */
+#define WM8990_SPK_ENA_BIT 12
+#define WM8990_OUT3_ENA 0x0800 /* OUT3_ENA */
+#define WM8990_OUT3_ENA_BIT 11
+#define WM8990_OUT4_ENA 0x0400 /* OUT4_ENA */
+#define WM8990_OUT4_ENA_BIT 10
+#define WM8990_LOUT_ENA 0x0200 /* LOUT_ENA */
+#define WM8990_LOUT_ENA_BIT 9
+#define WM8990_ROUT_ENA 0x0100 /* ROUT_ENA */
+#define WM8990_ROUT_ENA_BIT 8
+#define WM8990_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */
+#define WM8990_MICBIAS_ENA_BIT 4
+#define WM8990_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */
+#define WM8990_VREF_ENA 0x0001 /* VREF_ENA */
+#define WM8990_VREF_ENA_BIT 0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8990_PLL_ENA 0x8000 /* PLL_ENA */
+#define WM8990_PLL_ENA_BIT 15
+#define WM8990_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8990_TSHUT_ENA_BIT 14
+#define WM8990_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8990_TSHUT_OPDIS_BIT 13
+#define WM8990_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8990_OPCLK_ENA_BIT 11
+#define WM8990_AINL_ENA 0x0200 /* AINL_ENA */
+#define WM8990_AINL_ENA_BIT 9
+#define WM8990_AINR_ENA 0x0100 /* AINR_ENA */
+#define WM8990_AINR_ENA_BIT 8
+#define WM8990_LIN34_ENA 0x0080 /* LIN34_ENA */
+#define WM8990_LIN34_ENA_BIT 7
+#define WM8990_LIN12_ENA 0x0040 /* LIN12_ENA */
+#define WM8990_LIN12_ENA_BIT 6
+#define WM8990_RIN34_ENA 0x0020 /* RIN34_ENA */
+#define WM8990_RIN34_ENA_BIT 5
+#define WM8990_RIN12_ENA 0x0010 /* RIN12_ENA */
+#define WM8990_RIN12_ENA_BIT 4
+#define WM8990_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8990_ADCL_ENA_BIT 1
+#define WM8990_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8990_ADCR_ENA_BIT 0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8990_LON_ENA 0x2000 /* LON_ENA */
+#define WM8990_LON_ENA_BIT 13
+#define WM8990_LOP_ENA 0x1000 /* LOP_ENA */
+#define WM8990_LOP_ENA_BIT 12
+#define WM8990_RON_ENA 0x0800 /* RON_ENA */
+#define WM8990_RON_ENA_BIT 11
+#define WM8990_ROP_ENA 0x0400 /* ROP_ENA */
+#define WM8990_ROP_ENA_BIT 10
+#define WM8990_LOPGA_ENA 0x0080 /* LOPGA_ENA */
+#define WM8990_LOPGA_ENA_BIT 7
+#define WM8990_ROPGA_ENA 0x0040 /* ROPGA_ENA */
+#define WM8990_ROPGA_ENA_BIT 6
+#define WM8990_LOMIX_ENA 0x0020 /* LOMIX_ENA */
+#define WM8990_LOMIX_ENA_BIT 5
+#define WM8990_ROMIX_ENA 0x0010 /* ROMIX_ENA */
+#define WM8990_ROMIX_ENA_BIT 4
+#define WM8990_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8990_DACL_ENA_BIT 1
+#define WM8990_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8990_DACR_ENA_BIT 0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8990_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8990_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8990_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8990_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8990_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8990_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8990_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8990_AIF_WL_16BITS (0 << 5)
+#define WM8990_AIF_WL_20BITS (1 << 5)
+#define WM8990_AIF_WL_24BITS (2 << 5)
+#define WM8990_AIF_WL_32BITS (3 << 5)
+#define WM8990_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8990_AIF_TMF_RIGHTJ (0 << 3)
+#define WM8990_AIF_TMF_LEFTJ (1 << 3)
+#define WM8990_AIF_TMF_I2S (2 << 3)
+#define WM8990_AIF_TMF_DSP (3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8990_DACL_SRC 0x8000 /* DACL_SRC */
+#define WM8990_DACR_SRC 0x4000 /* DACR_SRC */
+#define WM8990_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8990_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8990_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST */
+#define WM8990_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8990_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8990_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8990_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8990_LOOPBACK 0x0001 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8990_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8990_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8990_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */
+#define WM8990_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8990_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8990_BCLK_DIV_1 (0x0 << 1)
+#define WM8990_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8990_BCLK_DIV_2 (0x2 << 1)
+#define WM8990_BCLK_DIV_3 (0x3 << 1)
+#define WM8990_BCLK_DIV_4 (0x4 << 1)
+#define WM8990_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8990_BCLK_DIV_6 (0x6 << 1)
+#define WM8990_BCLK_DIV_8 (0x7 << 1)
+#define WM8990_BCLK_DIV_11 (0x8 << 1)
+#define WM8990_BCLK_DIV_12 (0x9 << 1)
+#define WM8990_BCLK_DIV_16 (0xA << 1)
+#define WM8990_BCLK_DIV_22 (0xB << 1)
+#define WM8990_BCLK_DIV_24 (0xC << 1)
+#define WM8990_BCLK_DIV_32 (0xD << 1)
+#define WM8990_BCLK_DIV_44 (0xE << 1)
+#define WM8990_BCLK_DIV_48 (0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8990_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8990_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8990_CLK_FORCE 0x2000 /* CLK_FORCE */
+#define WM8990_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */
+#define WM8990_MCLK_DIV_1 (0 << 11)
+#define WM8990_MCLK_DIV_2 (2 << 11)
+#define WM8990_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8990_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV */
+#define WM8990_ADC_CLKDIV_1 (0 << 5)
+#define WM8990_ADC_CLKDIV_1_5 (1 << 5)
+#define WM8990_ADC_CLKDIV_2 (2 << 5)
+#define WM8990_ADC_CLKDIV_3 (3 << 5)
+#define WM8990_ADC_CLKDIV_4 (4 << 5)
+#define WM8990_ADC_CLKDIV_5_5 (5 << 5)
+#define WM8990_ADC_CLKDIV_6 (6 << 5)
+#define WM8990_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */
+#define WM8990_DAC_CLKDIV_1 (0 << 2)
+#define WM8990_DAC_CLKDIV_1_5 (1 << 2)
+#define WM8990_DAC_CLKDIV_2 (2 << 2)
+#define WM8990_DAC_CLKDIV_3 (3 << 2)
+#define WM8990_DAC_CLKDIV_4 (4 << 2)
+#define WM8990_DAC_CLKDIV_5_5 (5 << 2)
+#define WM8990_DAC_CLKDIV_6 (6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8990_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8990_AIF_MSTR2 0x4000 /* AIF_MSTR2 */
+#define WM8990_AIF_SEL 0x2000 /* AIF_SEL */
+#define WM8990_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */
+#define WM8990_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8990_ALRCGPIO1 0x8000 /* ALRCGPIO1 */
+#define WM8990_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */
+#define WM8990_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8990_DACLRC_DIR 0x0800 /* DACLRC_DIR */
+#define WM8990_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8990_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */
+#define WM8990_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8990_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8990_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8990_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */
+#define WM8990_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */
+#define WM8990_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8990_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8990_DACR_DATINV 0x0001 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8990_DAC_VU 0x0100 /* DAC_VU */
+#define WM8990_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8990_DACL_VOL_SHIFT 0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8990_DAC_VU 0x0100 /* DAC_VU */
+#define WM8990_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8990_DACR_VOL_SHIFT 0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8990_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL */
+#define WM8990_ADCL_DAC_SVOL_SHIFT 9
+#define WM8990_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL */
+#define WM8990_ADCR_DAC_SVOL_SHIFT 5
+#define WM8990_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */
+#define WM8990_ADC_TO_DACL_SHIFT 2
+#define WM8990_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */
+#define WM8990_ADC_TO_DACR_SHIFT 0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8990_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */
+#define WM8990_ADC_HPF_ENA_BIT 8
+#define WM8990_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */
+#define WM8990_ADC_HPF_CUT_SHIFT 5
+#define WM8990_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8990_ADCL_DATINV_BIT 1
+#define WM8990_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8990_ADCR_DATINV_BIT 0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8990_ADC_VU 0x0100 /* ADC_VU */
+#define WM8990_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8990_ADCL_VOL_SHIFT 0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8990_ADC_VU 0x0100 /* ADC_VU */
+#define WM8990_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8990_ADCR_VOL_SHIFT 0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8990_IRQ 0x1000 /* IRQ */
+#define WM8990_TEMPOK 0x0800 /* TEMPOK */
+#define WM8990_MICSHRT 0x0400 /* MICSHRT */
+#define WM8990_MICDET 0x0200 /* MICDET */
+#define WM8990_PLL_LCK 0x0100 /* PLL_LCK */
+#define WM8990_GPI8_STATUS 0x0080 /* GPI8_STATUS */
+#define WM8990_GPI7_STATUS 0x0040 /* GPI7_STATUS */
+#define WM8990_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */
+#define WM8990_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */
+#define WM8990_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */
+#define WM8990_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */
+#define WM8990_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */
+#define WM8990_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8990_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */
+#define WM8990_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */
+#define WM8990_GPIO2_PU 0x2000 /* GPIO2_PU */
+#define WM8990_GPIO2_PD 0x1000 /* GPIO2_PD */
+#define WM8990_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */
+#define WM8990_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */
+#define WM8990_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */
+#define WM8990_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8990_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8990_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8990_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */
+#define WM8990_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */
+#define WM8990_GPIO4_PU 0x2000 /* GPIO4_PU */
+#define WM8990_GPIO4_PD 0x1000 /* GPIO4_PD */
+#define WM8990_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */
+#define WM8990_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */
+#define WM8990_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */
+#define WM8990_GPIO3_PU 0x0020 /* GPIO3_PU */
+#define WM8990_GPIO3_PD 0x0010 /* GPIO3_PD */
+#define WM8990_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8990_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */
+#define WM8990_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */
+#define WM8990_GPIO6_PU 0x2000 /* GPIO6_PU */
+#define WM8990_GPIO6_PD 0x1000 /* GPIO6_PD */
+#define WM8990_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */
+#define WM8990_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */
+#define WM8990_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */
+#define WM8990_GPIO5_PU 0x0020 /* GPIO5_PU */
+#define WM8990_GPIO5_PD 0x0010 /* GPIO5_PD */
+#define WM8990_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8990_RD_3W_ENA 0x8000 /* RD_3W_ENA */
+#define WM8990_MODE_3W4W 0x4000 /* MODE_3W4W */
+#define WM8990_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */
+#define WM8990_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */
+#define WM8990_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */
+#define WM8990_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */
+#define WM8990_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */
+#define WM8990_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */
+#define WM8990_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8990_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */
+#define WM8990_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */
+#define WM8990_GPI7_ENA 0x0001 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8990_IRQ_INV 0x1000 /* IRQ_INV */
+#define WM8990_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8990_MICSHRT_POL 0x0400 /* MICSHRT_POL */
+#define WM8990_MICDET_POL 0x0200 /* MICDET_POL */
+#define WM8990_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */
+#define WM8990_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8990_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8990_GPIO6_POL 0x0020 /* GPIO6_POL */
+#define WM8990_GPIO5_POL 0x0010 /* GPIO5_POL */
+#define WM8990_GPIO4_POL 0x0008 /* GPIO4_POL */
+#define WM8990_GPIO3_POL 0x0004 /* GPIO3_POL */
+#define WM8990_GPIO2_POL 0x0002 /* GPIO2_POL */
+#define WM8990_GPIO1_POL 0x0001 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_LI12MUTE 0x0080 /* LI12MUTE */
+#define WM8990_LI12MUTE_BIT 7
+#define WM8990_LI12ZC 0x0040 /* LI12ZC */
+#define WM8990_LI12ZC_BIT 6
+#define WM8990_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */
+#define WM8990_LIN12VOL_SHIFT 0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_LI34MUTE 0x0080 /* LI34MUTE */
+#define WM8990_LI34MUTE_BIT 7
+#define WM8990_LI34ZC 0x0040 /* LI34ZC */
+#define WM8990_LI34ZC_BIT 6
+#define WM8990_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */
+#define WM8990_LIN34VOL_SHIFT 0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_RI12MUTE 0x0080 /* RI12MUTE */
+#define WM8990_RI12MUTE_BIT 7
+#define WM8990_RI12ZC 0x0040 /* RI12ZC */
+#define WM8990_RI12ZC_BIT 6
+#define WM8990_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */
+#define WM8990_RIN12VOL_SHIFT 0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_RI34MUTE 0x0080 /* RI34MUTE */
+#define WM8990_RI34MUTE_BIT 7
+#define WM8990_RI34ZC 0x0040 /* RI34ZC */
+#define WM8990_RI34ZC_BIT 6
+#define WM8990_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */
+#define WM8990_RIN34VOL_SHIFT 0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_LOZC 0x0080 /* LOZC */
+#define WM8990_LOZC_BIT 7
+#define WM8990_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
+#define WM8990_LOUTVOL_SHIFT 0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_ROZC 0x0080 /* ROZC */
+#define WM8990_ROZC_BIT 7
+#define WM8990_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
+#define WM8990_ROUTVOL_SHIFT 0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8990_LONMUTE 0x0040 /* LONMUTE */
+#define WM8990_LONMUTE_BIT 6
+#define WM8990_LOPMUTE 0x0020 /* LOPMUTE */
+#define WM8990_LOPMUTE_BIT 5
+#define WM8990_LOATTN 0x0010 /* LOATTN */
+#define WM8990_LOATTN_BIT 4
+#define WM8990_RONMUTE 0x0004 /* RONMUTE */
+#define WM8990_RONMUTE_BIT 2
+#define WM8990_ROPMUTE 0x0002 /* ROPMUTE */
+#define WM8990_ROPMUTE_BIT 1
+#define WM8990_ROATTN 0x0001 /* ROATTN */
+#define WM8990_ROATTN_BIT 0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8990_OUT3MUTE 0x0020 /* OUT3MUTE */
+#define WM8990_OUT3MUTE_BIT 5
+#define WM8990_OUT3ATTN 0x0010 /* OUT3ATTN */
+#define WM8990_OUT3ATTN_BIT 4
+#define WM8990_OUT4MUTE 0x0002 /* OUT4MUTE */
+#define WM8990_OUT4MUTE_BIT 1
+#define WM8990_OUT4ATTN 0x0001 /* OUT4ATTN */
+#define WM8990_OUT4ATTN_BIT 0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_LOPGAZC 0x0080 /* LOPGAZC */
+#define WM8990_LOPGAZC_BIT 7
+#define WM8990_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */
+#define WM8990_LOPGAVOL_SHIFT 0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_ROPGAZC 0x0080 /* ROPGAZC */
+#define WM8990_ROPGAZC_BIT 7
+#define WM8990_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */
+#define WM8990_ROPGAVOL_SHIFT 0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8990_SPKATTN_MASK 0x0003 /* SPKATTN - [1:0] */
+#define WM8990_SPKATTN_SHIFT 0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8990_CDMODE 0x0100 /* CDMODE */
+#define WM8990_CDMODE_BIT 8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8990_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */
+#define WM8990_DCGAIN_SHIFT 3
+#define WM8990_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */
+#define WM8990_ACGAIN_SHIFT 0
+
+/*
+ * R38 (0x26) - ClassD4
+ */
+#define WM8990_SPKZC_MASK 0x0001 /* SPKZC */
+#define WM8990_SPKZC_SHIFT 7 /* SPKZC */
+#define WM8990_SPKVOL_MASK 0x007F /* SPKVOL - [6:0] */
+#define WM8990_SPKVOL_SHIFT 0 /* SPKVOL - [6:0] */
+
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8990_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */
+#define WM8990_AINLMODE_SHIFT 2
+#define WM8990_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */
+#define WM8990_AINRMODE_SHIFT 0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8990_LMP4 0x0080 /* LMP4 */
+#define WM8990_LMP4_BIT 7 /* LMP4 */
+#define WM8990_LMN3 0x0040 /* LMN3 */
+#define WM8990_LMN3_BIT 6 /* LMN3 */
+#define WM8990_LMP2 0x0020 /* LMP2 */
+#define WM8990_LMP2_BIT 5 /* LMP2 */
+#define WM8990_LMN1 0x0010 /* LMN1 */
+#define WM8990_LMN1_BIT 4 /* LMN1 */
+#define WM8990_RMP4 0x0008 /* RMP4 */
+#define WM8990_RMP4_BIT 3 /* RMP4 */
+#define WM8990_RMN3 0x0004 /* RMN3 */
+#define WM8990_RMN3_BIT 2 /* RMN3 */
+#define WM8990_RMP2 0x0002 /* RMP2 */
+#define WM8990_RMP2_BIT 1 /* RMP2 */
+#define WM8990_RMN1 0x0001 /* RMN1 */
+#define WM8990_RMN1_BIT 0 /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8990_L34MNB 0x0100 /* L34MNB */
+#define WM8990_L34MNB_BIT 8
+#define WM8990_L34MNBST 0x0080 /* L34MNBST */
+#define WM8990_L34MNBST_BIT 7
+#define WM8990_L12MNB 0x0020 /* L12MNB */
+#define WM8990_L12MNB_BIT 5
+#define WM8990_L12MNBST 0x0010 /* L12MNBST */
+#define WM8990_L12MNBST_BIT 4
+#define WM8990_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */
+#define WM8990_LDBVOL_SHIFT 0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8990_R34MNB 0x0100 /* R34MNB */
+#define WM8990_R34MNB_BIT 8
+#define WM8990_R34MNBST 0x0080 /* R34MNBST */
+#define WM8990_R34MNBST_BIT 7
+#define WM8990_R12MNB 0x0020 /* R12MNB */
+#define WM8990_R12MNB_BIT 5
+#define WM8990_R12MNBST 0x0010 /* R12MNBST */
+#define WM8990_R12MNBST_BIT 4
+#define WM8990_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */
+#define WM8990_RDBVOL_SHIFT 0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8990_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */
+#define WM8990_LI2BVOL_SHIFT 6
+#define WM8990_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */
+#define WM8990_LR4BVOL_SHIFT 3
+#define WM8990_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */
+#define WM8990_LL4BVOL_SHIFT 0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8990_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */
+#define WM8990_RI2BVOL_SHIFT 6
+#define WM8990_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */
+#define WM8990_RL4BVOL_SHIFT 3
+#define WM8990_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */
+#define WM8990_RR4BVOL_SHIFT 0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8990_LRBLO 0x0080 /* LRBLO */
+#define WM8990_LRBLO_BIT 7
+#define WM8990_LLBLO 0x0040 /* LLBLO */
+#define WM8990_LLBLO_BIT 6
+#define WM8990_LRI3LO 0x0020 /* LRI3LO */
+#define WM8990_LRI3LO_BIT 5
+#define WM8990_LLI3LO 0x0010 /* LLI3LO */
+#define WM8990_LLI3LO_BIT 4
+#define WM8990_LR12LO 0x0008 /* LR12LO */
+#define WM8990_LR12LO_BIT 3
+#define WM8990_LL12LO 0x0004 /* LL12LO */
+#define WM8990_LL12LO_BIT 2
+#define WM8990_LDLO 0x0001 /* LDLO */
+#define WM8990_LDLO_BIT 0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8990_RLBRO 0x0080 /* RLBRO */
+#define WM8990_RLBRO_BIT 7
+#define WM8990_RRBRO 0x0040 /* RRBRO */
+#define WM8990_RRBRO_BIT 6
+#define WM8990_RLI3RO 0x0020 /* RLI3RO */
+#define WM8990_RLI3RO_BIT 5
+#define WM8990_RRI3RO 0x0010 /* RRI3RO */
+#define WM8990_RRI3RO_BIT 4
+#define WM8990_RL12RO 0x0008 /* RL12RO */
+#define WM8990_RL12RO_BIT 3
+#define WM8990_RR12RO 0x0004 /* RR12RO */
+#define WM8990_RR12RO_BIT 2
+#define WM8990_RDRO 0x0001 /* RDRO */
+#define WM8990_RDRO_BIT 0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8990_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */
+#define WM8990_LLI3LOVOL_SHIFT 6
+#define WM8990_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */
+#define WM8990_LR12LOVOL_SHIFT 3
+#define WM8990_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */
+#define WM8990_LL12LOVOL_SHIFT 0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8990_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */
+#define WM8990_RRI3ROVOL_SHIFT 6
+#define WM8990_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */
+#define WM8990_RL12ROVOL_SHIFT 3
+#define WM8990_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */
+#define WM8990_RR12ROVOL_SHIFT 0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8990_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */
+#define WM8990_LRI3LOVOL_SHIFT 6
+#define WM8990_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */
+#define WM8990_LRBLOVOL_SHIFT 3
+#define WM8990_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */
+#define WM8990_LLBLOVOL_SHIFT 0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8990_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */
+#define WM8990_RLI3ROVOL_SHIFT 6
+#define WM8990_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */
+#define WM8990_RLBROVOL_SHIFT 3
+#define WM8990_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */
+#define WM8990_RRBROVOL_SHIFT 0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8990_VSEL_MASK 0x0180 /* VSEL - [8:7] */
+#define WM8990_LI4O3 0x0020 /* LI4O3 */
+#define WM8990_LI4O3_BIT 5
+#define WM8990_LPGAO3 0x0010 /* LPGAO3 */
+#define WM8990_LPGAO3_BIT 4
+#define WM8990_RI4O4 0x0002 /* RI4O4 */
+#define WM8990_RI4O4_BIT 1
+#define WM8990_RPGAO4 0x0001 /* RPGAO4 */
+#define WM8990_RPGAO4_BIT 0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8990_LLOPGALON 0x0040 /* LLOPGALON */
+#define WM8990_LLOPGALON_BIT 6
+#define WM8990_LROPGALON 0x0020 /* LROPGALON */
+#define WM8990_LROPGALON_BIT 5
+#define WM8990_LOPLON 0x0010 /* LOPLON */
+#define WM8990_LOPLON_BIT 4
+#define WM8990_LR12LOP 0x0004 /* LR12LOP */
+#define WM8990_LR12LOP_BIT 2
+#define WM8990_LL12LOP 0x0002 /* LL12LOP */
+#define WM8990_LL12LOP_BIT 1
+#define WM8990_LLOPGALOP 0x0001 /* LLOPGALOP */
+#define WM8990_LLOPGALOP_BIT 0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8990_RROPGARON 0x0040 /* RROPGARON */
+#define WM8990_RROPGARON_BIT 6
+#define WM8990_RLOPGARON 0x0020 /* RLOPGARON */
+#define WM8990_RLOPGARON_BIT 5
+#define WM8990_ROPRON 0x0010 /* ROPRON */
+#define WM8990_ROPRON_BIT 4
+#define WM8990_RL12ROP 0x0004 /* RL12ROP */
+#define WM8990_RL12ROP_BIT 2
+#define WM8990_RR12ROP 0x0002 /* RR12ROP */
+#define WM8990_RR12ROP_BIT 1
+#define WM8990_RROPGAROP 0x0001 /* RROPGAROP */
+#define WM8990_RROPGAROP_BIT 0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8990_LB2SPK 0x0080 /* LB2SPK */
+#define WM8990_LB2SPK_BIT 7
+#define WM8990_RB2SPK 0x0040 /* RB2SPK */
+#define WM8990_RB2SPK_BIT 6
+#define WM8990_LI2SPK 0x0020 /* LI2SPK */
+#define WM8990_LI2SPK_BIT 5
+#define WM8990_RI2SPK 0x0010 /* RI2SPK */
+#define WM8990_RI2SPK_BIT 4
+#define WM8990_LOPGASPK 0x0008 /* LOPGASPK */
+#define WM8990_LOPGASPK_BIT 3
+#define WM8990_ROPGASPK 0x0004 /* ROPGASPK */
+#define WM8990_ROPGASPK_BIT 2
+#define WM8990_LDSPK 0x0002 /* LDSPK */
+#define WM8990_LDSPK_BIT 1
+#define WM8990_RDSPK 0x0001 /* RDSPK */
+#define WM8990_RDSPK_BIT 0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8990_VROI 0x0001 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8990_DIS_LLINE 0x0020 /* DIS_LLINE */
+#define WM8990_DIS_RLINE 0x0010 /* DIS_RLINE */
+#define WM8990_DIS_OUT3 0x0008 /* DIS_OUT3 */
+#define WM8990_DIS_OUT4 0x0004 /* DIS_OUT4 */
+#define WM8990_DIS_LOUT 0x0002 /* DIS_LOUT */
+#define WM8990_DIS_ROUT 0x0001 /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8990_SOFTST 0x0040 /* SOFTST */
+#define WM8990_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8990_BUFDCOPEN 0x0004 /* BUFDCOPEN */
+#define WM8990_POBCTRL 0x0002 /* POBCTRL */
+#define WM8990_VMIDTOG 0x0001 /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8990_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */
+#define WM8990_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */
+#define WM8990_MCD 0x0004 /* MCD */
+#define WM8990_MBSEL 0x0001 /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8990_SDM 0x0080 /* SDM */
+#define WM8990_PRESCALE 0x0040 /* PRESCALE */
+#define WM8990_PLLN_MASK 0x000F /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8990_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8990_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8990_INMIXL_PWR_BIT 0
+#define WM8990_AINLMUX_PWR_BIT 1
+#define WM8990_INMIXR_PWR_BIT 2
+#define WM8990_AINRMUX_PWR_BIT 3
+
+struct wm8990_setup_data {
+ unsigned i2c_bus;
+ unsigned short i2c_address;
+};
+
+#define WM8990_MCLK_DIV 0
+#define WM8990_DACCLK_DIV 1
+#define WM8990_ADCCLK_DIV 2
+#define WM8990_BCLK_DIV 3
+
+extern struct snd_soc_dai wm8990_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8990;
+
+#endif /* __WM8990REGISTERDEFS_H__ */
+/*------------------------------ END OF FILE ---------------------------------*/
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 76c1e2d33e7..ffb471e420e 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -2,21 +2,16 @@
* wm9712.c -- ALSA Soc WM9712 codec support
*
* Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
- *
- * Revision history
- * 4th Feb 2006 Initial version.
*/
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -25,6 +20,7 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include "wm9712.h"
#define WM9712_VERSION "0.4"
@@ -351,7 +347,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"},
@@ -429,38 +425,31 @@ static const char *audio_map[][3] = {
{"HPOUTR", NULL, "Headphone PGA"},
{"Headphone PGA", NULL, "Right HP Mixer"},
- /* mono hp mixer */
- {"Mono HP Mixer", NULL, "Left HP Mixer"},
- {"Mono HP Mixer", NULL, "Right HP Mixer"},
+ /* mono mixer */
+ {"Mono Mixer", NULL, "Left HP Mixer"},
+ {"Mono Mixer", NULL, "Right HP Mixer"},
/* Out3 Mux */
{"Out3 Mux", "Left", "Left HP Mixer"},
{"Out3 Mux", "Mono", "Phone Mixer"},
- {"Out3 Mux", "Left + Right", "Mono HP Mixer"},
+ {"Out3 Mux", "Left + Right", "Mono Mixer"},
{"Out 3 PGA", NULL, "Out3 Mux"},
{"OUT3", NULL, "Out 3 PGA"},
/* speaker Mux */
{"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
- {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"},
+ {"Speaker Mux", "Headphone Mix", "Mono Mixer"},
{"Speaker PGA", NULL, "Speaker Mux"},
{"LOUT2", NULL, "Speaker PGA"},
{"ROUT2", NULL, "Speaker PGA"},
-
- {NULL, NULL, NULL},
};
static int wm9712_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm9712_dapm_widgets,
+ ARRAY_SIZE(wm9712_dapm_widgets));
- /* set up audio path connects */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -541,7 +530,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
-struct snd_soc_codec_dai wm9712_dai[] = {
+struct snd_soc_dai wm9712_dai[] = {
{
.name = "AC97 HiFi",
.type = SND_SOC_DAI_AC97_BUS,
@@ -574,23 +563,23 @@ struct snd_soc_codec_dai wm9712_dai[] = {
};
EXPORT_SYMBOL_GPL(wm9712_dai);
-static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -598,12 +587,12 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
if (try_warm && soc_ac97_ops.warm_reset) {
soc_ac97_ops.warm_reset(codec->ac97);
- if (!(ac97_read(codec, 0) & 0x8000))
+ if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
soc_ac97_ops.reset(codec->ac97);
- if (ac97_read(codec, 0) & 0x8000)
+ if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
@@ -618,7 +607,7 @@ static int wm9712_soc_suspend(struct platform_device *pdev,
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -635,7 +624,7 @@ static int wm9712_soc_resume(struct platform_device *pdev)
return ret;
}
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret == 0) {
/* Sync reg_cache with the hardware after cold reset */
@@ -647,8 +636,8 @@ static int wm9712_soc_resume(struct platform_device *pdev)
}
}
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_ON);
return ret;
}
@@ -682,7 +671,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
codec->num_dai = ARRAY_SIZE(wm9712_dai);
codec->write = ac97_write;
codec->read = ac97_read;
- codec->dapm_event = wm9712_dapm_event;
+ codec->set_bias_level = wm9712_set_bias_level;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -706,7 +695,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
/* set alc mux to none */
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm9712_add_controls(codec);
wm9712_add_widgets(codec);
ret = snd_soc_register_card(socdev);
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
index 719105d61e6..d29e8a18ca6 100644
--- a/sound/soc/codecs/wm9712.h
+++ b/sound/soc/codecs/wm9712.h
@@ -8,7 +8,7 @@
#define WM9712_DAI_AC97_HIFI 0
#define WM9712_DAI_AC97_AUX 1
-extern struct snd_soc_codec_dai wm9712_dai[2];
+extern struct snd_soc_dai wm9712_dai[2];
extern struct snd_soc_codec_device soc_codec_dev_wm9712;
#endif
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 1f241161445..aba402b3c99 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -2,17 +2,13 @@
* wm9713.c -- ALSA Soc WM9713 codec support
*
* Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 4th Feb 2006 Initial version.
- *
* Features:-
*
* o Support for AC97 Codec, Voice DAC and Aux DAC
@@ -422,8 +418,12 @@ SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
-SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1),
-SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1),
+SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
+SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
@@ -456,7 +456,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
SND_SOC_DAPM_VMID("VMID"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* left HP mixer */
{"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -586,9 +586,13 @@ static const char *audio_map[][3] = {
/* left ADC */
{"Left ADC", NULL, "Left Capture Source"},
+ {"Left Voice ADC", NULL, "Left ADC"},
+ {"Left HiFi ADC", NULL, "Left ADC"},
/* right ADC */
{"Right ADC", NULL, "Right Capture Source"},
+ {"Right Voice ADC", NULL, "Right ADC"},
+ {"Right HiFi ADC", NULL, "Right ADC"},
/* mic */
{"Mic A Pre Amp", NULL, "Mic A Source"},
@@ -607,21 +611,14 @@ static const char *audio_map[][3] = {
{"Capture Mono Mux", "Stereo", "Capture Mixer"},
{"Capture Mono Mux", "Left", "Left Capture Source"},
{"Capture Mono Mux", "Right", "Right Capture Source"},
-
- {NULL, NULL, NULL},
};
static int wm9713_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm9713_dapm_widgets,
+ ARRAY_SIZE(wm9713_dapm_widgets));
- /* set up audio path audio_mapnects */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -799,7 +796,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
-static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -810,7 +807,7 @@ static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
* Tristate the PCM DAI lines, tristate can be disabled by calling
* wm9713_set_dai_fmt()
*/
-static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
int tristate)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -826,7 +823,7 @@ static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
* Configure WM9713 clock dividers.
* Voice DAC needs 256 FS
*/
-static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -868,7 +865,7 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -886,7 +883,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
gpio |= 0x0018;
break;
case SND_SOC_DAIFMT_CBS_CFS:
- reg |= 0x0200;
+ reg |= 0x2000;
gpio |= 0x001a;
break;
case SND_SOC_DAIFMT_CBS_CFM:
@@ -959,17 +956,17 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
- u16 status;
-
- /* Gracefully shut down the voice interface. */
- status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
- ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
- schedule_timeout_interruptible(msecs_to_jiffies(1));
- ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
- ac97_write(codec, AC97_EXTENDED_MID, status);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 status;
+
+ /* Gracefully shut down the voice interface. */
+ status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
+ ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
+ ac97_write(codec, AC97_EXTENDED_MID, status);
}
static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
@@ -1011,15 +1008,24 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
}
-#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000)
+#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
#define WM9713_PCM_FORMATS \
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24_LE)
-struct snd_soc_codec_dai wm9713_dai[] = {
+struct snd_soc_dai wm9713_dai[] = {
{
.name = "AC97 HiFi",
.type = SND_SOC_DAI_AC97_BUS,
@@ -1061,13 +1067,13 @@ struct snd_soc_codec_dai wm9713_dai[] = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 1,
- .rates = WM9713_RATES,
+ .rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
.capture = {
.stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = WM9713_RATES,
+ .rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
.ops = {
.hw_params = wm9713_pcm_hw_params,
@@ -1086,44 +1092,44 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
if (try_warm && soc_ac97_ops.warm_reset) {
soc_ac97_ops.warm_reset(codec->ac97);
- if (!(ac97_read(codec, 0) & 0x8000))
+ if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
soc_ac97_ops.reset(codec->ac97);
- if (ac97_read(codec, 0) & 0x8000)
+ if (ac97_read(codec, 0) != wm9713_reg[0])
return -EIO;
return 0;
}
EXPORT_SYMBOL_GPL(wm9713_reset);
-static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9713_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 reg;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* enable thermal shutdown */
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
ac97_write(codec, AC97_EXTENDED_MID, reg);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* enable master bias and vmid */
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
ac97_write(codec, AC97_EXTENDED_MID, reg);
ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -1160,7 +1166,7 @@ static int wm9713_soc_resume(struct platform_device *pdev)
return ret;
}
- wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* do we need to re-start the PLL ? */
if (wm9713->pll_out)
@@ -1176,8 +1182,8 @@ static int wm9713_soc_resume(struct platform_device *pdev)
}
}
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
- wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_ON);
return ret;
}
@@ -1216,7 +1222,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
codec->num_dai = ARRAY_SIZE(wm9713_dai);
codec->write = ac97_write;
codec->read = ac97_read;
- codec->dapm_event = wm9713_dapm_event;
+ codec->set_bias_level = wm9713_set_bias_level;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1238,7 +1244,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
goto reset_err;
}
- wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* unmute the adc - move to kcontrol */
reg = ac97_read(codec, AC97_CD) & 0x7fff;
diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h
index d357b6c8134..63b8d81756e 100644
--- a/sound/soc/codecs/wm9713.h
+++ b/sound/soc/codecs/wm9713.h
@@ -46,7 +46,7 @@
#define WM9713_DAI_PCM_VOICE 2
extern struct snd_soc_codec_device soc_codec_dev_wm9713;
-extern struct snd_soc_codec_dai wm9713_dai[3];
+extern struct snd_soc_dai wm9713_dai[3];
int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 20680c551aa..8f7e3383490 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,6 +1,6 @@
config SND_DAVINCI_SOC
tristate "SoC Audio for the TI DAVINCI chip"
- depends on ARCH_DAVINCI && SND_SOC
+ depends on ARCH_DAVINCI
help
Say Y or M if you want to add support for codecs attached to
the DAVINCI AC97 or I2S interface. You will also need
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index fcd16524033..9e6062cd6b5 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -1,7 +1,7 @@
/*
* ASoC driver for TI DAVINCI EVM platform
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -19,9 +19,8 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <asm/mach-types.h>
#include <asm/dma.h>
-#include <asm/arch/hardware.h>
+#include <mach/hardware.h>
#include "../codecs/tlv320aic3x.h"
#include "davinci-pcm.h"
@@ -33,24 +32,24 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret = 0;
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
SND_SOC_DAIFMT_IB_NF);
if (ret < 0)
return ret;
/* set the codec system clock */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
@@ -71,7 +70,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
};
/* davinci-evm machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* Headphone connected to HPLOUT, HPROUT */
{"Headphone Jack", NULL, "HPLOUT"},
{"Headphone Jack", NULL, "HPROUT"},
@@ -90,36 +89,30 @@ static const char *audio_map[][3] = {
{"LINE2L", NULL, "Line In"},
{"LINE1R", NULL, "Line In"},
{"LINE2R", NULL, "Line In"},
-
- {NULL, NULL, NULL},
};
/* Logic for a aic3x as connected on a davinci-evm */
static int evm_aic3x_init(struct snd_soc_codec *codec)
{
- int i;
-
/* Add davinci-evm specific widgets */
- for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+ ARRAY_SIZE(aic3x_dapm_widgets));
/* Set up davinci-evm specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
/* not connected */
- snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
- snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
- snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+ snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+ snd_soc_dapm_disable_pin(codec, "HPLCOM");
+ snd_soc_dapm_disable_pin(codec, "HPRCOM");
/* always connected */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
- snd_soc_dapm_set_endpoint(codec, "Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
- snd_soc_dapm_set_endpoint(codec, "Line In", 1);
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Line Out");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_enable_pin(codec, "Line In");
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -143,6 +136,7 @@ static struct snd_soc_machine snd_soc_machine_evm = {
/* evm audio private data */
static struct aic3x_setup_data evm_aic3x_setup = {
+ .i2c_bus = 0,
.i2c_address = 0x1b,
};
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index c421774b33e..abb5fedb0b1 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -1,7 +1,7 @@
/*
* ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -147,7 +147,7 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
static int davinci_i2s_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
cpu_dai->dma_data = dev->dma_params[substream->stream];
@@ -155,7 +155,7 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
return 0;
}
-static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
@@ -256,7 +256,7 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
break;
default:
- printk(KERN_WARNING "davinci-i2s: unsupported PCM format");
+ printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
return -EINVAL;
}
@@ -295,11 +295,12 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static int davinci_i2s_probe(struct platform_device *pdev)
+static int davinci_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
struct davinci_mcbsp_dev *dev;
struct resource *mem, *ioarea;
struct evm_snd_platform_data *pdata;
@@ -356,11 +357,12 @@ err_release_region:
return ret;
}
-static void davinci_i2s_remove(struct platform_device *pdev)
+static void davinci_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
struct resource *mem;
@@ -376,7 +378,7 @@ static void davinci_i2s_remove(struct platform_device *pdev)
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
-struct snd_soc_cpu_dai davinci_i2s_dai = {
+struct snd_soc_dai davinci_i2s_dai = {
.name = "davinci-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index 9592d17db32..241648ce887 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -1,7 +1,7 @@
/*
* ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -12,6 +12,6 @@
#ifndef _DAVINCI_I2S_H
#define _DAVINCI_I2S_H
-extern struct snd_soc_cpu_dai davinci_i2s_dai;
+extern struct snd_soc_dai davinci_i2s_dai;
#endif
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 6a76927c997..76feaa65737 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -1,7 +1,7 @@
/*
* ALSA PCM interface for the TI DAVINCI processor
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -350,7 +350,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
static u64 davinci_pcm_dmamask = 0xffffffff;
static int davinci_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret;
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 8d6a45e75a6..62cb4eb07e3 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -1,7 +1,7 @@
/*
* ALSA PCM interface for the TI DAVINCI processor
*
- * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 257101f44e9..bba9546ba5f 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,8 +1,9 @@
-menu "ALSA SoC audio for Freescale SOCs"
+config SND_SOC_OF_SIMPLE
+ tristate
config SND_SOC_MPC8610
bool "ALSA SoC support for the MPC8610 SOC"
- depends on SND_SOC && MPC8610_HPCD
+ depends on MPC8610_HPCD
default y if MPC8610
help
Say Y if you want to add support for codecs attached to the SSI
@@ -17,4 +18,9 @@ config SND_SOC_MPC8610_HPCD
help
Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
-endmenu
+config SND_SOC_MPC5200_I2S
+ tristate "Freescale MPC5200 PSC in I2S mode driver"
+ select SND_SOC_OF_SIMPLE
+ depends on SND_SOC && PPC_MPC52xx
+ help
+ Say Y here to support the MPC5200 PSCs in I2S mode.
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 62f680a4a77..035da4afec3 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -1,6 +1,11 @@
+# Simple machine driver that extracts configuration from the OF device tree
+obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o
+
# 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
+obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
+
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 78de7168d2b..d2d3da9729f 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -132,12 +132,17 @@ struct fsl_dma_private {
* 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.
+ *
+ * Note that we specify SNDRV_PCM_INFO_JOINT_DUPLEX here, but only because a
+ * limitation in the SSI driver requires the sample rates for playback and
+ * capture to be the same.
*/
static const struct snd_pcm_hardware fsl_dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID,
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_JOINT_DUPLEX,
.formats = FSLDMA_PCM_FORMATS,
.rates = FSLDMA_PCM_RATES,
.rate_min = 5512,
@@ -282,7 +287,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* 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,
+static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
static u64 fsl_dma_dmamask = DMA_BIT_MASK(32);
@@ -322,14 +327,75 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
* fsl_dma_open: open a new substream.
*
* Each substream has its own DMA buffer.
+ *
+ * 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.
*/
static int fsl_dma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private;
+ struct ccsr_dma_channel __iomem *dma_channel;
dma_addr_t ld_buf_phys;
+ u64 temp_link; /* Pointer to next link descriptor */
+ u32 mr;
unsigned int channel;
int ret = 0;
+ unsigned int i;
/*
* Reject any DMA buffer whose size is not a multiple of the period
@@ -390,68 +456,74 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware);
runtime->private_data = dma_private;
+ /* Program the fixed DMA controller parameters */
+
+ dma_channel = dma_private->dma_channel;
+
+ 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->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);
+
+ 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 transferring.
+ *
+ * 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_hw_params: allocate the DMA buffer and the DMA link descriptors.
+ * fsl_dma_hw_params: continue initializing the DMA links
*
- * 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.
+ * This function obtains hardware parameters about the opened stream and
+ * programs the DMA controller accordingly.
*
* 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
@@ -463,11 +535,8 @@ static int fsl_dma_hw_params(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 temp_addr; /* Pointer to next period */
- u64 temp_link; /* Pointer to next link descriptor */
- u32 mr; /* Temporary variable for MR register */
unsigned int i;
@@ -485,8 +554,6 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
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
@@ -502,16 +569,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* 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);
@@ -519,51 +581,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
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;
}
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
index 430a6ce8b0d..385d4a42603 100644
--- a/sound/soc/fsl/fsl_dma.h
+++ b/sound/soc/fsl/fsl_dma.h
@@ -126,7 +126,7 @@ struct fsl_dma_link_descriptor {
u8 res[4]; /* Reserved */
} __attribute__ ((aligned(32), packed));
-/* DMA information needed to create a snd_soc_cpu_dai object
+/* DMA information needed to create a snd_soc_dai object
*
* ssi_stx_phys: bus address of SSI STX register to use
* ssi_srx_phys: bus address of SSI SRX register to use
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index f588545698f..157a7895ffa 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -67,6 +67,8 @@
* @ssi: pointer to the SSI's registers
* @ssi_phys: physical address of the SSI registers
* @irq: IRQ of this SSI
+ * @first_stream: pointer to the stream that was opened first
+ * @second_stream: pointer to second stream
* @dev: struct device pointer
* @playback: the number of playback streams opened
* @capture: the number of capture streams opened
@@ -79,10 +81,12 @@ struct fsl_ssi_private {
struct ccsr_ssi __iomem *ssi;
dma_addr_t ssi_phys;
unsigned int irq;
+ struct snd_pcm_substream *first_stream;
+ struct snd_pcm_substream *second_stream;
struct device *dev;
unsigned int playback;
unsigned int capture;
- struct snd_soc_cpu_dai cpu_dai;
+ struct snd_soc_dai cpu_dai;
struct device_attribute dev_attr;
struct {
@@ -342,6 +346,49 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
*/
}
+ if (!ssi_private->first_stream)
+ ssi_private->first_stream = substream;
+ else {
+ /* This is the second stream open, so we need to impose sample
+ * rate and maybe sample size constraints. Note that this can
+ * cause a race condition if the second stream is opened before
+ * the first stream is fully initialized.
+ *
+ * We provide some protection by checking to make sure the first
+ * stream is initialized, but it's not perfect. ALSA sometimes
+ * re-initializes the driver with a different sample rate or
+ * size. If the second stream is opened before the first stream
+ * has received its final parameters, then the second stream may
+ * be constrained to the wrong sample rate or size.
+ *
+ * FIXME: This code does not handle opening and closing streams
+ * repeatedly. If you open two streams and then close the first
+ * one, you may not be able to open another stream until you
+ * close the second one as well.
+ */
+ struct snd_pcm_runtime *first_runtime =
+ ssi_private->first_stream->runtime;
+
+ if (!first_runtime->rate || !first_runtime->sample_bits) {
+ dev_err(substream->pcm->card->dev,
+ "set sample rate and size in %s stream first\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? "capture" : "playback");
+ return -EAGAIN;
+ }
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ first_runtime->rate, first_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ first_runtime->sample_bits,
+ first_runtime->sample_bits);
+
+ ssi_private->second_stream = substream;
+ }
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ssi_private->playback++;
@@ -371,18 +418,16 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
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));
+ if (substream == ssi_private->first_stream) {
+ u32 wl;
- clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+ /* The SSI should always be disabled at this points (SSIEN=0) */
+ wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ /* In synchronous mode, the SSI uses STCCR for capture */
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;
}
@@ -407,9 +452,13 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
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);
+ clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+ setbits32(&ssi->scr,
+ CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
} else {
- setbits32(&ssi->scr, CCSR_SSI_SCR_RE);
+ clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+ setbits32(&ssi->scr,
+ CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
/*
* I think we need this delay to allow time for the SSI
@@ -452,6 +501,11 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
ssi_private->capture--;
+ if (ssi_private->first_stream == substream)
+ ssi_private->first_stream = ssi_private->second_stream;
+
+ ssi_private->second_stream = NULL;
+
/*
* If this is the last active substream, disable the SSI and release
* the IRQ.
@@ -479,7 +533,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
* @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,
+static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -497,7 +551,7 @@ static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
*
* @format: one of SND_SOC_DAIFMT_xxx
*/
-static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
+static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
{
return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
}
@@ -505,7 +559,7 @@ static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
/**
* fsl_ssi_dai_template: template CPU DAI for the SSI
*/
-static struct snd_soc_cpu_dai fsl_ssi_dai_template = {
+static struct snd_soc_dai fsl_ssi_dai_template = {
.playback = {
/* The SSI does not support monaural audio. */
.channels_min = 2,
@@ -569,15 +623,15 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev,
}
/**
- * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure
+ * fsl_ssi_create_dai: create a snd_soc_dai structure
*
- * This function is called by the machine driver to create a snd_soc_cpu_dai
+ * This function is called by the machine driver to create a snd_soc_dai
* structure. The function creates an ssi_private object, which contains
- * the snd_soc_cpu_dai. It also creates the sysfs statistics device.
+ * the snd_soc_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_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
{
- struct snd_soc_cpu_dai *fsl_ssi_dai;
+ struct snd_soc_dai *fsl_ssi_dai;
struct fsl_ssi_private *ssi_private;
int ret = 0;
struct device_attribute *dev_attr;
@@ -588,7 +642,7 @@ struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
return NULL;
}
memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
- sizeof(struct snd_soc_cpu_dai));
+ sizeof(struct snd_soc_dai));
fsl_ssi_dai = &ssi_private->cpu_dai;
dev_attr = &ssi_private->dev_attr;
@@ -623,11 +677,11 @@ struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
/**
- * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object
+ * fsl_ssi_destroy_dai: destroy the snd_soc_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)
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
{
struct fsl_ssi_private *ssi_private =
container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index c5ce88e1565..83b44d700e3 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -217,8 +217,8 @@ struct fsl_ssi_info {
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);
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai);
#endif
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
new file mode 100644
index 00000000000..86923299bc1
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -0,0 +1,884 @@
+/*
+ * Freescale MPC5200 PSC in I2S mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-of-simple.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <asm/mpc52xx_psc.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
+MODULE_LICENSE("GPL");
+
+/**
+ * PSC_I2S_RATES: sample rates supported by the I2S
+ *
+ * This driver currently only supports the PSC 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 PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
+ SNDRV_PCM_RATE_CONTINUOUS)
+
+/**
+ * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
+ */
+#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
+ SNDRV_PCM_FMTBIT_S32_BE)
+
+/**
+ * psc_i2s_stream - Data specific to a single stream (playback or capture)
+ * @active: flag indicating if the stream is active
+ * @psc_i2s: pointer back to parent psc_i2s data structure
+ * @bcom_task: bestcomm task structure
+ * @irq: irq number for bestcomm task
+ * @period_start: physical address of start of DMA region
+ * @period_end: physical address of end of DMA region
+ * @period_next_pt: physical address of next DMA buffer to enqueue
+ * @period_bytes: size of DMA period in bytes
+ */
+struct psc_i2s_stream {
+ int active;
+ struct psc_i2s *psc_i2s;
+ struct bcom_task *bcom_task;
+ int irq;
+ struct snd_pcm_substream *stream;
+ dma_addr_t period_start;
+ dma_addr_t period_end;
+ dma_addr_t period_next_pt;
+ dma_addr_t period_current_pt;
+ int period_bytes;
+};
+
+/**
+ * psc_i2s - Private driver data
+ * @name: short name for this device ("PSC0", "PSC1", etc)
+ * @psc_regs: pointer to the PSC's registers
+ * @fifo_regs: pointer to the PSC's FIFO registers
+ * @irq: IRQ of this PSC
+ * @dev: struct device pointer
+ * @dai: the CPU DAI for this device
+ * @sicr: Base value used in serial interface control register; mode is ORed
+ * with this value.
+ * @playback: Playback stream context data
+ * @capture: Capture stream context data
+ */
+struct psc_i2s {
+ char name[32];
+ struct mpc52xx_psc __iomem *psc_regs;
+ struct mpc52xx_psc_fifo __iomem *fifo_regs;
+ unsigned int irq;
+ struct device *dev;
+ struct snd_soc_dai dai;
+ spinlock_t lock;
+ u32 sicr;
+
+ /* per-stream data */
+ struct psc_i2s_stream playback;
+ struct psc_i2s_stream capture;
+
+ /* Statistics */
+ struct {
+ int overrun_count;
+ int underrun_count;
+ } stats;
+};
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
+{
+ struct psc_i2s *psc_i2s = _psc_i2s;
+ struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+ u16 isr;
+
+ isr = in_be16(&regs->mpc52xx_psc_isr);
+
+ /* Playback underrun error */
+ if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
+ psc_i2s->stats.underrun_count++;
+
+ /* Capture overrun error */
+ if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
+ psc_i2s->stats.overrun_count++;
+
+ out_8(&regs->command, 4 << 4); /* reset the error status */
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
+ * @s: pointer to stream private data structure
+ *
+ * Enqueues another audio period buffer into the bestcomm queue.
+ *
+ * Note: The routine must only be called when there is space available in
+ * the queue. Otherwise the enqueue will fail and the audio ring buffer
+ * will get out of sync
+ */
+static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
+{
+ struct bcom_bd *bd;
+
+ /* Prepare and enqueue the next buffer descriptor */
+ bd = bcom_prepare_next_buffer(s->bcom_task);
+ bd->status = s->period_bytes;
+ bd->data[0] = s->period_next_pt;
+ bcom_submit_next_buffer(s->bcom_task, NULL);
+
+ /* Update for next period */
+ s->period_next_pt += s->period_bytes;
+ if (s->period_next_pt >= s->period_end)
+ s->period_next_pt = s->period_start;
+}
+
+/* Bestcomm DMA irq handler */
+static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
+{
+ struct psc_i2s_stream *s = _psc_i2s_stream;
+
+ /* For each finished period, dequeue the completed period buffer
+ * and enqueue a new one in it's place. */
+ while (bcom_buffer_done(s->bcom_task)) {
+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+ s->period_current_pt += s->period_bytes;
+ if (s->period_current_pt >= s->period_end)
+ s->period_current_pt = s->period_start;
+ psc_i2s_bcom_enqueue_next_buffer(s);
+ bcom_enable(s->bcom_task);
+ }
+
+ /* If the stream is active, then also inform the PCM middle layer
+ * of the period finished event. */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * psc_i2s_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 PSC registers.
+ */
+static int psc_i2s_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ int rc;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
+
+ if (!psc_i2s->playback.active &&
+ !psc_i2s->capture.active) {
+ /* Setup the IRQs */
+ rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
+ "psc-i2s-status", psc_i2s);
+ rc |= request_irq(psc_i2s->capture.irq,
+ &psc_i2s_bcom_irq, IRQF_SHARED,
+ "psc-i2s-capture", &psc_i2s->capture);
+ rc |= request_irq(psc_i2s->playback.irq,
+ &psc_i2s_bcom_irq, IRQF_SHARED,
+ "psc-i2s-playback", &psc_i2s->playback);
+ if (rc) {
+ free_irq(psc_i2s->irq, psc_i2s);
+ free_irq(psc_i2s->capture.irq,
+ &psc_i2s->capture);
+ free_irq(psc_i2s->playback.irq,
+ &psc_i2s->playback);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ u32 mode;
+
+ dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+ " periods=%i buffer_size=%i buffer_bytes=%i\n",
+ __func__, substream, params_period_size(params),
+ params_period_bytes(params), params_periods(params),
+ params_buffer_size(params), params_buffer_bytes(params));
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_BE:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
+ break;
+ default:
+ dev_dbg(psc_i2s->dev, "invalid format\n");
+ return -EINVAL;
+ }
+ out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+/**
+ * psc_i2s_trigger: start and stop the DMA transfer.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the DMA
+ * transfer of data.
+ */
+static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct psc_i2s_stream *s;
+ struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+ u16 imr;
+ u8 psc_cmd;
+ long flags;
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture;
+ else
+ s = &psc_i2s->playback;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
+ " stream_id=%i\n",
+ substream, cmd, substream->pstr->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ s->period_bytes = frames_to_bytes(runtime,
+ runtime->period_size);
+ s->period_start = virt_to_phys(runtime->dma_area);
+ s->period_end = s->period_start +
+ (s->period_bytes * runtime->periods);
+ s->period_next_pt = s->period_start;
+ s->period_current_pt = s->period_start;
+ s->active = 1;
+
+ /* First; reset everything */
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ out_8(&regs->command, MPC52xx_PSC_RST_RX);
+ out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+ } else {
+ out_8(&regs->command, MPC52xx_PSC_RST_TX);
+ out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+ }
+
+ /* Next, fill up the bestcomm bd queue and enable DMA.
+ * This will begin filling the PSC's fifo. */
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ bcom_gen_bd_rx_reset(s->bcom_task);
+ else
+ bcom_gen_bd_tx_reset(s->bcom_task);
+ while (!bcom_queue_full(s->bcom_task))
+ psc_i2s_bcom_enqueue_next_buffer(s);
+ bcom_enable(s->bcom_task);
+
+ /* Due to errata in the i2s mode; need to line up enabling
+ * the transmitter with a transition on the frame sync
+ * line */
+
+ spin_lock_irqsave(&psc_i2s->lock, flags);
+ /* first make sure it is low */
+ while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
+ ;
+ /* then wait for the transition to high */
+ while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
+ ;
+ /* Finally, enable the PSC.
+ * Receiver must always be enabled; even when we only want
+ * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */
+ psc_cmd = MPC52xx_PSC_RX_ENABLE;
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ psc_cmd |= MPC52xx_PSC_TX_ENABLE;
+ out_8(&regs->command, psc_cmd);
+ spin_unlock_irqrestore(&psc_i2s->lock, flags);
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* Turn off the PSC */
+ s->active = 0;
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!psc_i2s->playback.active) {
+ out_8(&regs->command, 2 << 4); /* reset rx */
+ out_8(&regs->command, 3 << 4); /* reset tx */
+ out_8(&regs->command, 4 << 4); /* reset err */
+ }
+ } else {
+ out_8(&regs->command, 3 << 4); /* reset tx */
+ out_8(&regs->command, 4 << 4); /* reset err */
+ if (!psc_i2s->capture.active)
+ out_8(&regs->command, 2 << 4); /* reset rx */
+ }
+
+ bcom_disable(s->bcom_task);
+ while (!bcom_queue_empty(s->bcom_task))
+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+ break;
+
+ default:
+ dev_dbg(psc_i2s->dev, "invalid command\n");
+ return -EINVAL;
+ }
+
+ /* Update interrupt enable settings */
+ imr = 0;
+ if (psc_i2s->playback.active)
+ imr |= MPC52xx_PSC_IMR_TXEMP;
+ if (psc_i2s->capture.active)
+ imr |= MPC52xx_PSC_IMR_ORERR;
+ out_be16(&regs->isr_imr.imr, imr);
+
+ return 0;
+}
+
+/**
+ * psc_i2s_shutdown: shutdown the data transfer on a stream
+ *
+ * Shutdown the PSC if there are no other substreams open.
+ */
+static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
+
+ /*
+ * If this is the last active substream, disable the PSC and release
+ * the IRQ.
+ */
+ if (!psc_i2s->playback.active &&
+ !psc_i2s->capture.active) {
+
+ /* Disable all interrupts and reset the PSC */
+ out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
+ out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */
+ out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */
+ out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
+ out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+
+ /* Release irqs */
+ free_irq(psc_i2s->irq, psc_i2s);
+ free_irq(psc_i2s->capture.irq, &psc_i2s->capture);
+ free_irq(psc_i2s->playback.irq, &psc_i2s->playback);
+ }
+}
+
+/**
+ * psc_i2s_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 psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct psc_i2s *psc_i2s = cpu_dai->private_data;
+ dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
+ cpu_dai, dir);
+ return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
+}
+
+/**
+ * psc_i2s_set_fmt: set the serial format.
+ *
+ * This function is called by the machine driver to tell us what serial
+ * format to use.
+ *
+ * This driver only supports I2S mode. Return an error if the format is
+ * not SND_SOC_DAIFMT_I2S.
+ *
+ * @format: one of SND_SOC_DAIFMT_xxx
+ */
+static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
+{
+ struct psc_i2s *psc_i2s = cpu_dai->private_data;
+ dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
+ cpu_dai, format);
+ return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
+}
+
+/* ---------------------------------------------------------------------
+ * ALSA SoC Bindings
+ *
+ * - Digital Audio Interface (DAI) template
+ * - create/destroy dai hooks
+ */
+
+/**
+ * psc_i2s_dai_template: template CPU Digital Audio Interface
+ */
+static struct snd_soc_dai psc_i2s_dai_template = {
+ .type = SND_SOC_DAI_I2S,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PSC_I2S_RATES,
+ .formats = PSC_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PSC_I2S_RATES,
+ .formats = PSC_I2S_FORMATS,
+ },
+ .ops = {
+ .startup = psc_i2s_startup,
+ .hw_params = psc_i2s_hw_params,
+ .hw_free = psc_i2s_hw_free,
+ .shutdown = psc_i2s_shutdown,
+ .trigger = psc_i2s_trigger,
+ },
+ .dai_ops = {
+ .set_sysclk = psc_i2s_set_sysclk,
+ .set_fmt = psc_i2s_set_fmt,
+ },
+};
+
+/* ---------------------------------------------------------------------
+ * The PSC I2S 'ASoC platform' driver
+ *
+ * Can be referenced by an 'ASoC machine' driver
+ * This driver only deals with the audio bus; it doesn't have any
+ * interaction with the attached codec
+ */
+
+static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_max = 1024 * 1024,
+ .period_bytes_min = 32,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .fifo_size = 0,
+};
+
+static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct psc_i2s_stream *s;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture;
+ else
+ s = &psc_i2s->playback;
+
+ snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
+
+ s->stream = substream;
+ return 0;
+}
+
+static int psc_i2s_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct psc_i2s_stream *s;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture;
+ else
+ s = &psc_i2s->playback;
+
+ s->stream = NULL;
+ return 0;
+}
+
+static snd_pcm_uframes_t
+psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct psc_i2s_stream *s;
+ dma_addr_t count;
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture;
+ else
+ s = &psc_i2s->playback;
+
+ count = s->period_current_pt - s->period_start;
+
+ return bytes_to_frames(substream->runtime, count);
+}
+
+static struct snd_pcm_ops psc_i2s_pcm_ops = {
+ .open = psc_i2s_pcm_open,
+ .close = psc_i2s_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .pointer = psc_i2s_pcm_pointer,
+};
+
+static u64 psc_i2s_pcm_dmamask = 0xffffffff;
+static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
+ int rc = 0;
+
+ dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
+ card, dai, pcm);
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &psc_i2s_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (pcm->streams[0].substream) {
+ rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+ &pcm->streams[0].substream->dma_buffer);
+ if (rc)
+ goto playback_alloc_err;
+ }
+
+ if (pcm->streams[1].substream) {
+ rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+ &pcm->streams[1].substream->dma_buffer);
+ if (rc)
+ goto capture_alloc_err;
+ }
+
+ return 0;
+
+ capture_alloc_err:
+ if (pcm->streams[0].substream)
+ snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+ playback_alloc_err:
+ dev_err(card->dev, "Cannot allocate buffer(s)\n");
+ return -ENOMEM;
+}
+
+static void psc_i2s_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_pcm_substream *substream;
+ int stream;
+
+ dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (substream) {
+ snd_dma_free_pages(&substream->dma_buffer);
+ substream->dma_buffer.area = NULL;
+ substream->dma_buffer.addr = 0;
+ }
+ }
+}
+
+struct snd_soc_platform psc_i2s_pcm_soc_platform = {
+ .name = "mpc5200-psc-audio",
+ .pcm_ops = &psc_i2s_pcm_ops,
+ .pcm_new = &psc_i2s_pcm_new,
+ .pcm_free = &psc_i2s_pcm_free,
+};
+
+/* ---------------------------------------------------------------------
+ * Sysfs attributes for debugging
+ */
+
+static ssize_t psc_i2s_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+
+ return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x "
+ "tfnum=%i tfstat=0x%.4x\n",
+ in_be16(&psc_i2s->psc_regs->sr_csr.status),
+ in_be32(&psc_i2s->psc_regs->sicr),
+ in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
+ in_be16(&psc_i2s->fifo_regs->rfstat),
+ in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
+ in_be16(&psc_i2s->fifo_regs->tfstat));
+}
+
+static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name)
+{
+ if (strcmp(name, "playback_underrun") == 0)
+ return &psc_i2s->stats.underrun_count;
+ if (strcmp(name, "capture_overrun") == 0)
+ return &psc_i2s->stats.overrun_count;
+
+ return NULL;
+}
+
+static ssize_t psc_i2s_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+ int *attrib;
+
+ attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
+ if (!attrib)
+ return 0;
+
+ return sprintf(buf, "%i\n", *attrib);
+}
+
+static ssize_t psc_i2s_stat_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+ int *attrib;
+
+ attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
+ if (!attrib)
+ return 0;
+
+ *attrib = simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
+DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
+DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
+
+/* ---------------------------------------------------------------------
+ * OF platform bus binding code:
+ * - Probe/remove operations
+ * - OF device match table
+ */
+static int __devinit psc_i2s_of_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ phys_addr_t fifo;
+ struct psc_i2s *psc_i2s;
+ struct resource res;
+ int size, psc_id, irq, rc;
+ const __be32 *prop;
+ void __iomem *regs;
+
+ dev_dbg(&op->dev, "probing psc i2s device\n");
+
+ /* Get the PSC ID */
+ prop = of_get_property(op->node, "cell-index", &size);
+ if (!prop || size < sizeof *prop)
+ return -ENODEV;
+ psc_id = be32_to_cpu(*prop);
+
+ /* Fetch the registers and IRQ of the PSC */
+ irq = irq_of_parse_and_map(op->node, 0);
+ if (of_address_to_resource(op->node, 0, &res)) {
+ dev_err(&op->dev, "Missing reg property\n");
+ return -ENODEV;
+ }
+ regs = ioremap(res.start, 1 + res.end - res.start);
+ if (!regs) {
+ dev_err(&op->dev, "Could not map registers\n");
+ return -ENODEV;
+ }
+
+ /* Allocate and initialize the driver private data */
+ psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
+ if (!psc_i2s) {
+ iounmap(regs);
+ return -ENOMEM;
+ }
+ spin_lock_init(&psc_i2s->lock);
+ psc_i2s->irq = irq;
+ psc_i2s->psc_regs = regs;
+ psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
+ psc_i2s->dev = &op->dev;
+ psc_i2s->playback.psc_i2s = psc_i2s;
+ psc_i2s->capture.psc_i2s = psc_i2s;
+ snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
+
+ /* Fill out the CPU DAI structure */
+ memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
+ psc_i2s->dai.private_data = psc_i2s;
+ psc_i2s->dai.name = psc_i2s->name;
+ psc_i2s->dai.id = psc_id;
+
+ /* Find the address of the fifo data registers and setup the
+ * DMA tasks */
+ fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
+ psc_i2s->capture.bcom_task =
+ bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
+ psc_i2s->playback.bcom_task =
+ bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
+ if (!psc_i2s->capture.bcom_task ||
+ !psc_i2s->playback.bcom_task) {
+ dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
+ iounmap(regs);
+ kfree(psc_i2s);
+ return -ENODEV;
+ }
+
+ /* Disable all interrupts and reset the PSC */
+ out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
+ out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */
+ out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */
+ out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
+ out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+
+ /* Configure the serial interface mode; defaulting to CODEC8 mode */
+ psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
+ MPC52xx_PSC_SICR_CLKPOL;
+ if (of_get_property(op->node, "fsl,cellslave", NULL))
+ psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE |
+ MPC52xx_PSC_SICR_GENCLK;
+ out_be32(&psc_i2s->psc_regs->sicr,
+ psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
+
+ /* Check for the codec handle. If it is not present then we
+ * are done */
+ if (!of_get_property(op->node, "codec-handle", NULL))
+ return 0;
+
+ /* Set up mode register;
+ * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
+ * Second write: register Normal mode for non loopback
+ */
+ out_8(&psc_i2s->psc_regs->mode, 0);
+ out_8(&psc_i2s->psc_regs->mode, 0);
+
+ /* Set the TX and RX fifo alarm thresholds */
+ out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100);
+ out_8(&psc_i2s->fifo_regs->rfcntl, 0x4);
+ out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100);
+ out_8(&psc_i2s->fifo_regs->tfcntl, 0x7);
+
+ /* Lookup the IRQ numbers */
+ psc_i2s->playback.irq =
+ bcom_get_task_irq(psc_i2s->playback.bcom_task);
+ psc_i2s->capture.irq =
+ bcom_get_task_irq(psc_i2s->capture.bcom_task);
+
+ /* Save what we've done so it can be found again later */
+ dev_set_drvdata(&op->dev, psc_i2s);
+
+ /* Register the SYSFS files */
+ rc = device_create_file(psc_i2s->dev, &dev_attr_status);
+ rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
+ rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
+ if (rc)
+ dev_info(psc_i2s->dev, "error creating sysfs files\n");
+
+ /* Tell the ASoC OF helpers about it */
+ of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
+ &psc_i2s->dai);
+
+ return 0;
+}
+
+static int __devexit psc_i2s_of_remove(struct of_device *op)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
+
+ dev_dbg(&op->dev, "psc_i2s_remove()\n");
+
+ bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
+ bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
+
+ iounmap(psc_i2s->psc_regs);
+ iounmap(psc_i2s->fifo_regs);
+ kfree(psc_i2s);
+ dev_set_drvdata(&op->dev, NULL);
+
+ return 0;
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id psc_i2s_match[] __devinitdata = {
+ { .compatible = "fsl,mpc5200-psc-i2s", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, psc_i2s_match);
+
+static struct of_platform_driver psc_i2s_driver = {
+ .match_table = psc_i2s_match,
+ .probe = psc_i2s_of_probe,
+ .remove = __devexit_p(psc_i2s_of_remove),
+ .driver = {
+ .name = "mpc5200-psc-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup and teardown; simply register the of_platform driver
+ * for the PSC in I2S mode.
+ */
+static int __init psc_i2s_init(void)
+{
+ return of_register_platform_driver(&psc_i2s_driver);
+}
+module_init(psc_i2s_init);
+
+static void __exit psc_i2s_exit(void)
+{
+ of_unregister_platform_driver(&psc_i2s_driver);
+}
+module_exit(psc_i2s_exit);
+
+
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index a00aac7a71f..94f89debde1 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -58,9 +58,9 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
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,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
@@ -68,10 +68,6 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
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,
@@ -96,62 +92,52 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
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 snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_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;
- }
+ ret = snd_soc_dai_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;
- }
+ ret = snd_soc_dai_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;
- }
+ ret = snd_soc_dai_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;
- }
+ ret = snd_soc_dai_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;
@@ -170,9 +156,9 @@ int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
/* Restore the signal routing */
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[0], 0);
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[1], 0);
switch (machine_data->ssi_id) {
@@ -182,7 +168,7 @@ int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
break;
case 1:
clrsetbits_be32(&machine_data->guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
+ CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
break;
}
@@ -240,6 +226,8 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
struct fsl_ssi_info ssi_info;
struct fsl_dma_info dma_info;
int ret = -ENODEV;
+ unsigned int playback_dma_channel;
+ unsigned int capture_dma_channel;
machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
if (!machine_data)
@@ -391,8 +379,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
goto error;
}
- /* Find the DMA channels to use. For now, we always use the first DMA
- controller. */
+ /* Find the DMA channels to use. Both SSIs need to use the same DMA
+ * controller, so let's use DMA#1.
+ */
for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {
iprop = of_get_property(dma_np, "cell-index", NULL);
if (iprop && (*iprop == 0)) {
@@ -407,14 +396,19 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
}
machine_data->dma_id = *iprop;
+ /* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA
+ * channels 2 and 3. This is just how the MPC8610 is wired
+ * internally.
+ */
+ playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2;
+ capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3;
+
/*
- * Find the DMA channels to use. For now, we always use DMA channel 0
- * for playback, and DMA channel 1 for capture.
+ * Find the DMA channels to use.
*/
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)) {
+ if (iprop && (*iprop == playback_dma_channel)) {
/* 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] =
@@ -422,7 +416,7 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
machine_data->dma_channel_id[0] = *iprop;
continue;
}
- if (iprop && (*iprop == 1)) {
+ if (iprop && (*iprop == capture_dma_channel)) {
/* 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] =
diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c
new file mode 100644
index 00000000000..0382fdac51c
--- /dev/null
+++ b/sound/soc/fsl/soc-of-simple.c
@@ -0,0 +1,171 @@
+/*
+ * OF helpers for ALSA SoC Layer
+ *
+ * Copyright (C) 2008, Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-of-simple.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
+
+static DEFINE_MUTEX(of_snd_soc_mutex);
+static LIST_HEAD(of_snd_soc_device_list);
+static int of_snd_soc_next_index;
+
+struct of_snd_soc_device {
+ int id;
+ struct list_head list;
+ struct snd_soc_device device;
+ struct snd_soc_machine machine;
+ struct snd_soc_dai_link dai_link;
+ struct platform_device *pdev;
+ struct device_node *platform_node;
+ struct device_node *codec_node;
+};
+
+static struct snd_soc_ops of_snd_soc_ops = {
+};
+
+static struct of_snd_soc_device *
+of_snd_soc_get_device(struct device_node *codec_node)
+{
+ struct of_snd_soc_device *of_soc;
+
+ list_for_each_entry(of_soc, &of_snd_soc_device_list, list) {
+ if (of_soc->codec_node == codec_node)
+ return of_soc;
+ }
+
+ of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL);
+ if (!of_soc)
+ return NULL;
+
+ /* Initialize the structure and add it to the global list */
+ of_soc->codec_node = codec_node;
+ of_soc->id = of_snd_soc_next_index++;
+ of_soc->machine.dai_link = &of_soc->dai_link;
+ of_soc->machine.num_links = 1;
+ of_soc->device.machine = &of_soc->machine;
+ of_soc->dai_link.ops = &of_snd_soc_ops;
+ list_add(&of_soc->list, &of_snd_soc_device_list);
+
+ return of_soc;
+}
+
+static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ /* Only register the device if both the codec and platform have
+ * been registered */
+ if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
+ return;
+
+ pr_info("platform<-->codec match achieved; registering machine\n");
+
+ pdev = platform_device_alloc("soc-audio", of_soc->id);
+ if (!pdev) {
+ pr_err("of_soc: platform_device_alloc() failed\n");
+ return;
+ }
+
+ pdev->dev.platform_data = of_soc;
+ platform_set_drvdata(pdev, &of_soc->device);
+ of_soc->device.dev = &pdev->dev;
+
+ /* The ASoC device is complete; register it */
+ rc = platform_device_add(pdev);
+ if (rc) {
+ pr_err("of_soc: platform_device_add() failed\n");
+ return;
+ }
+
+}
+
+int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
+ void *codec_data, struct snd_soc_dai *dai,
+ struct device_node *node)
+{
+ struct of_snd_soc_device *of_soc;
+ int rc = 0;
+
+ pr_info("registering ASoC codec driver: %s\n", node->full_name);
+
+ mutex_lock(&of_snd_soc_mutex);
+ of_soc = of_snd_soc_get_device(node);
+ if (!of_soc) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Store the codec data */
+ of_soc->device.codec_data = codec_data;
+ of_soc->device.codec_dev = codec_dev;
+ of_soc->dai_link.name = (char *)node->name;
+ of_soc->dai_link.stream_name = (char *)node->name;
+ of_soc->dai_link.codec_dai = dai;
+
+ /* Now try to register the SoC device */
+ of_snd_soc_register_device(of_soc);
+
+ out:
+ mutex_unlock(&of_snd_soc_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
+
+int of_snd_soc_register_platform(struct snd_soc_platform *platform,
+ struct device_node *node,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct of_snd_soc_device *of_soc;
+ struct device_node *codec_node;
+ const phandle *handle;
+ int len, rc = 0;
+
+ pr_info("registering ASoC platform driver: %s\n", node->full_name);
+
+ handle = of_get_property(node, "codec-handle", &len);
+ if (!handle || len < sizeof(handle))
+ return -ENODEV;
+ codec_node = of_find_node_by_phandle(*handle);
+ if (!codec_node)
+ return -ENODEV;
+ pr_info("looking for codec: %s\n", codec_node->full_name);
+
+ mutex_lock(&of_snd_soc_mutex);
+ of_soc = of_snd_soc_get_device(codec_node);
+ if (!of_soc) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ of_soc->platform_node = node;
+ of_soc->dai_link.cpu_dai = cpu_dai;
+ of_soc->device.platform = platform;
+ of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
+
+ /* Now try to register the SoC device */
+ of_snd_soc_register_device(of_soc);
+
+ out:
+ mutex_unlock(&of_snd_soc_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 0230d83e8e5..8b7766b998d 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -1,5 +1,3 @@
-menu "SoC Audio for the Texas Instruments OMAP"
-
config SND_OMAP_SOC
tristate "SoC Audio for the Texas Instruments OMAP chips"
depends on ARCH_OMAP && SND_SOC
@@ -16,4 +14,10 @@ config SND_OMAP_SOC_N810
help
Say Y if you want to add support for SoC audio on Nokia N810.
-endmenu
+config SND_OMAP_SOC_OSK5912
+ tristate "SoC Audio support for omap osk5912"
+ depends on SND_OMAP_SOC && MACH_OMAP_OSK
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y if you want to add support for SoC audio on osk5912.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index d8d8d58075e..e09d1f297f6 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -7,5 +7,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
# OMAP Machine Support
snd-soc-n810-objs := n810.o
+snd-soc-osk5912-objs := osk5912.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
+obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 6533563a601..fae3ad36e0b 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -29,16 +29,16 @@
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/gpio.h>
-#include <asm/arch/mcbsp.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+#include <mach/mcbsp.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"
#include "../codecs/tlv320aic3x.h"
-#define RX44_HEADSET_AMP_GPIO 10
-#define RX44_SPEAKER_AMP_GPIO 101
+#define N810_HEADSET_AMP_GPIO 10
+#define N810_SPEAKER_AMP_GPIO 101
static struct clk *sys_clkout2;
static struct clk *sys_clkout2_src;
@@ -46,13 +46,26 @@ static struct clk *func96m_clk;
static int n810_spk_func;
static int n810_jack_func;
+static int n810_dmic_func;
static void n810_ext_control(struct snd_soc_codec *codec)
{
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func);
+ if (n810_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
+
+ if (n810_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ if (n810_dmic_func)
+ snd_soc_dapm_enable_pin(codec, "DMic");
+ else
+ snd_soc_dapm_disable_pin(codec, "DMic");
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int n810_startup(struct snd_pcm_substream *substream)
@@ -73,12 +86,12 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int err;
/* Set codec DAI configuration */
- err = codec_dai->dai_ops.set_fmt(codec_dai,
+ err = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
@@ -86,7 +99,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
return err;
/* Set cpu DAI configuration */
- err = cpu_dai->dai_ops.set_fmt(cpu_dai,
+ err = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
@@ -94,7 +107,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
return err;
/* Set the codec system clock for DAC and ADC */
- err = codec_dai->dai_ops.set_sysclk(codec_dai, 0, 12000000,
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
SND_SOC_CLOCK_IN);
return err;
@@ -150,13 +163,35 @@ static int n810_set_jack(struct snd_kcontrol *kcontrol,
return 1;
}
+static int n810_get_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = n810_dmic_func;
+
+ return 0;
+}
+
+static int n810_set_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (n810_dmic_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ n810_dmic_func = ucontrol->value.integer.value[0];
+ n810_ext_control(codec);
+
+ return 1;
+}
+
static int n810_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
- omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 1);
+ gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
else
- omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 0);
+ gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
return 0;
}
@@ -165,9 +200,9 @@ static int n810_jack_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
- omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 1);
+ gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
else
- omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 0);
+ gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
return 0;
}
@@ -175,21 +210,27 @@ static int n810_jack_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
+ SND_SOC_DAPM_MIC("DMic", NULL),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
{"Headphone Jack", NULL, "HPLOUT"},
{"Headphone Jack", NULL, "HPROUT"},
{"Ext Spk", NULL, "LLOUT"},
{"Ext Spk", NULL, "RLOUT"},
+
+ {"DMic Rate 64", NULL, "Mic Bias 2V"},
+ {"Mic Bias 2V", NULL, "DMic"},
};
static const char *spk_function[] = {"Off", "On"};
static const char *jack_function[] = {"Off", "Headphone"};
+static const char *input_function[] = {"ADC", "Digital Mic"};
static const struct soc_enum n810_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
};
static const struct snd_kcontrol_new aic33_n810_controls[] = {
@@ -197,6 +238,8 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = {
n810_get_spk, n810_set_spk),
SOC_ENUM_EXT("Jack Function", n810_enum[1],
n810_get_jack, n810_set_jack),
+ SOC_ENUM_EXT("Input Select", n810_enum[2],
+ n810_get_input, n810_set_input),
};
static int n810_aic33_init(struct snd_soc_codec *codec)
@@ -204,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec)
int i, err;
/* Not connected */
- snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
- snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
- snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+ snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
+ snd_soc_dapm_nc_pin(codec, "HPLCOM");
+ snd_soc_dapm_nc_pin(codec, "HPRCOM");
/* Add N810 specific controls */
for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
@@ -217,15 +260,13 @@ static int n810_aic33_init(struct snd_soc_codec *codec)
}
/* Add N810 specific widgets */
- for (i = 0; i < ARRAY_SIZE(aic33_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &aic33_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,
+ ARRAY_SIZE(aic33_dapm_widgets));
/* Set up N810 specific audio path audio_map */
- for (i = 0; i < ARRAY_SIZE(audio_map); i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -249,7 +290,10 @@ static struct snd_soc_machine snd_soc_machine_n810 = {
/* Audio private data */
static struct aic3x_setup_data n810_aic33_setup = {
+ .i2c_bus = 2,
.i2c_address = 0x18,
+ .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
+ .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
};
/* Audio subsystem */
@@ -267,7 +311,7 @@ static int __init n810_soc_init(void)
int err;
struct device *dev;
- if (!machine_is_nokia_n810())
+ if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
return -ENODEV;
n810_snd_device = platform_device_alloc("soc-audio", -1);
@@ -286,12 +330,14 @@ static int __init n810_soc_init(void)
sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
if (IS_ERR(sys_clkout2_src)) {
dev_err(dev, "Could not get sys_clkout2_src clock\n");
- return -ENODEV;
+ err = PTR_ERR(sys_clkout2_src);
+ goto err2;
}
sys_clkout2 = clk_get(dev, "sys_clkout2");
if (IS_ERR(sys_clkout2)) {
dev_err(dev, "Could not get sys_clkout2\n");
- goto err1;
+ err = PTR_ERR(sys_clkout2);
+ goto err3;
}
/*
* Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
@@ -300,31 +346,40 @@ static int __init n810_soc_init(void)
func96m_clk = clk_get(dev, "func_96m_ck");
if (IS_ERR(func96m_clk)) {
dev_err(dev, "Could not get func 96M clock\n");
- goto err2;
+ err = PTR_ERR(func96m_clk);
+ goto err4;
}
clk_set_parent(sys_clkout2_src, func96m_clk);
clk_set_rate(sys_clkout2, 12000000);
- if (omap_request_gpio(RX44_HEADSET_AMP_GPIO) < 0)
+ if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0)
BUG();
- if (omap_request_gpio(RX44_SPEAKER_AMP_GPIO) < 0)
+ if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)
BUG();
- omap_set_gpio_direction(RX44_HEADSET_AMP_GPIO, 0);
- omap_set_gpio_direction(RX44_SPEAKER_AMP_GPIO, 0);
+ gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
+ gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
return 0;
-err2:
+err4:
clk_put(sys_clkout2);
+err3:
+ clk_put(sys_clkout2_src);
+err2:
platform_device_del(n810_snd_device);
err1:
platform_device_put(n810_snd_device);
return err;
-
}
static void __exit n810_soc_exit(void)
{
+ gpio_free(N810_SPEAKER_AMP_GPIO);
+ gpio_free(N810_HEADSET_AMP_GPIO);
+ clk_put(sys_clkout2_src);
+ clk_put(sys_clkout2);
+ clk_put(func96m_clk);
+
platform_device_unregister(n810_snd_device);
}
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 40d87e6d0de..0a063a98a66 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -30,9 +30,9 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <asm/arch/control.h>
-#include <asm/arch/dma.h>
-#include <asm/arch/mcbsp.h>
+#include <mach/control.h>
+#include <mach/dma.h>
+#include <mach/mcbsp.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"
@@ -59,12 +59,7 @@ static struct omap_mcbsp_data mcbsp_data[NUM_LINKS];
* Stream DMA parameters. DMA request line and port address are set runtime
* since they are different between OMAP1 and later OMAPs
*/
-static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = {
-{
- { .name = "I2S PCM Stereo out", },
- { .name = "I2S PCM Stereo in", },
-},
-};
+static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2];
#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
static const int omap1_dma_reqs[][2] = {
@@ -84,11 +79,22 @@ static const unsigned long omap1_mcbsp_port[][2] = {
static const int omap1_dma_reqs[][2] = {};
static const unsigned long omap1_mcbsp_port[][2] = {};
#endif
-#if defined(CONFIG_ARCH_OMAP2420)
-static const int omap2420_dma_reqs[][2] = {
+
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
+static const int omap24xx_dma_reqs[][2] = {
{ OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX },
{ OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX },
+#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
+ { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX },
+ { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX },
+ { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX },
+#endif
};
+#else
+static const int omap24xx_dma_reqs[][2] = {};
+#endif
+
+#if defined(CONFIG_ARCH_OMAP2420)
static const unsigned long omap2420_mcbsp_port[][2] = {
{ OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
@@ -96,14 +102,47 @@ static const unsigned long omap2420_mcbsp_port[][2] = {
OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
};
#else
-static const int omap2420_dma_reqs[][2] = {};
static const unsigned long omap2420_mcbsp_port[][2] = {};
#endif
+#if defined(CONFIG_ARCH_OMAP2430)
+static const unsigned long omap2430_mcbsp_port[][2] = {
+ { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR },
+};
+#else
+static const unsigned long omap2430_mcbsp_port[][2] = {};
+#endif
+
+#if defined(CONFIG_ARCH_OMAP34XX)
+static const unsigned long omap34xx_mcbsp_port[][2] = {
+ { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR },
+};
+#else
+static const unsigned long omap34xx_mcbsp_port[][2] = {};
+#endif
+
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
int err = 0;
@@ -116,7 +155,7 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
if (!cpu_dai->active) {
@@ -128,7 +167,7 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
int err = 0;
@@ -157,7 +196,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
@@ -167,14 +206,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
dma = omap1_dma_reqs[bus_id][substream->stream];
port = omap1_mcbsp_port[bus_id][substream->stream];
} else if (cpu_is_omap2420()) {
- dma = omap2420_dma_reqs[bus_id][substream->stream];
+ dma = omap24xx_dma_reqs[bus_id][substream->stream];
port = omap2420_mcbsp_port[bus_id][substream->stream];
+ } else if (cpu_is_omap2430()) {
+ dma = omap24xx_dma_reqs[bus_id][substream->stream];
+ port = omap2430_mcbsp_port[bus_id][substream->stream];
+ } else if (cpu_is_omap343x()) {
+ dma = omap24xx_dma_reqs[bus_id][substream->stream];
+ port = omap34xx_mcbsp_port[bus_id][substream->stream];
} else {
- /*
- * TODO: Add support for 2430 and 3430
- */
return -ENODEV;
}
+ omap_mcbsp_dai_dma_params[id][substream->stream].name =
+ substream->stream ? "Audio Capture" : "Audio Playback";
omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
@@ -223,7 +267,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
* This must be called before _set_clkdiv and _set_sysclk since McBSP register
* cache is initialized here
*/
-static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -245,6 +289,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
regs->rcr2 |= RDATDLY(1);
regs->xcr2 |= XDATDLY(1);
break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* 0-bit data delay */
+ regs->rcr2 |= RDATDLY(0);
+ regs->xcr2 |= XDATDLY(0);
+ break;
default:
/* Unsupported data format */
return -EINVAL;
@@ -292,7 +341,7 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
return 0;
}
-static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -310,7 +359,7 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
int clk_id)
{
int sel_bit;
- u16 reg;
+ u16 reg, reg_devconf1 = OMAP243X_CONTROL_DEVCONF1;
if (cpu_class_is_omap1()) {
/* OMAP1's can use only external source clock */
@@ -320,6 +369,12 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
return 0;
}
+ if (cpu_is_omap2420() && mcbsp_data->bus_id > 1)
+ return -EINVAL;
+
+ if (cpu_is_omap343x())
+ reg_devconf1 = OMAP343X_CONTROL_DEVCONF1;
+
switch (mcbsp_data->bus_id) {
case 0:
reg = OMAP2_CONTROL_DEVCONF0;
@@ -329,25 +384,31 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
reg = OMAP2_CONTROL_DEVCONF0;
sel_bit = 6;
break;
- /* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */
+ case 2:
+ reg = reg_devconf1;
+ sel_bit = 0;
+ break;
+ case 3:
+ reg = reg_devconf1;
+ sel_bit = 2;
+ break;
+ case 4:
+ reg = reg_devconf1;
+ sel_bit = 4;
+ break;
default:
return -EINVAL;
}
- if (cpu_class_is_omap2()) {
- if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) {
- omap_ctrl_writel(omap_ctrl_readl(reg) &
- ~(1 << sel_bit), reg);
- } else {
- omap_ctrl_writel(omap_ctrl_readl(reg) |
- (1 << sel_bit), reg);
- }
- }
+ if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK)
+ omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg);
+ else
+ omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg);
return 0;
}
-static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq,
int dir)
{
@@ -376,37 +437,49 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
return err;
}
-struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS] = {
-{
- .name = "omap-mcbsp-dai",
- .id = 0,
- .type = SND_SOC_DAI_I2S,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = {
- .startup = omap_mcbsp_dai_startup,
- .shutdown = omap_mcbsp_dai_shutdown,
- .trigger = omap_mcbsp_dai_trigger,
- .hw_params = omap_mcbsp_dai_hw_params,
- },
- .dai_ops = {
- .set_fmt = omap_mcbsp_dai_set_dai_fmt,
- .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
- .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
- },
- .private_data = &mcbsp_data[0].bus_id,
-},
+#define OMAP_MCBSP_DAI_BUILDER(link_id) \
+{ \
+ .name = "omap-mcbsp-dai-(link_id)", \
+ .id = (link_id), \
+ .type = SND_SOC_DAI_I2S, \
+ .playback = { \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = OMAP_MCBSP_RATES, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .capture = { \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = OMAP_MCBSP_RATES, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = { \
+ .startup = omap_mcbsp_dai_startup, \
+ .shutdown = omap_mcbsp_dai_shutdown, \
+ .trigger = omap_mcbsp_dai_trigger, \
+ .hw_params = omap_mcbsp_dai_hw_params, \
+ }, \
+ .dai_ops = { \
+ .set_fmt = omap_mcbsp_dai_set_dai_fmt, \
+ .set_clkdiv = omap_mcbsp_dai_set_clkdiv, \
+ .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \
+ }, \
+ .private_data = &mcbsp_data[(link_id)].bus_id, \
+}
+
+struct snd_soc_dai omap_mcbsp_dai[] = {
+ OMAP_MCBSP_DAI_BUILDER(0),
+ OMAP_MCBSP_DAI_BUILDER(1),
+#if NUM_LINKS >= 3
+ OMAP_MCBSP_DAI_BUILDER(2),
+#endif
+#if NUM_LINKS == 5
+ OMAP_MCBSP_DAI_BUILDER(3),
+ OMAP_MCBSP_DAI_BUILDER(4),
+#endif
};
+
EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 9965fd4b042..df7ad13ba73 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -38,12 +38,18 @@ enum omap_mcbsp_div {
OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
};
-/*
- * REVISIT: Preparation for the ASoC v2. Let the number of available links to
- * be same than number of McBSP ports found in OMAP(s) we are compiling for.
- */
-#define NUM_LINKS 1
+#if defined(CONFIG_ARCH_OMAP2420)
+#define NUM_LINKS 2
+#endif
+#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
+#undef NUM_LINKS
+#define NUM_LINKS 3
+#endif
+#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
+#undef NUM_LINKS
+#define NUM_LINKS 5
+#endif
-extern struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS];
+extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
#endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 62370202c64..e9084fdd208 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -27,7 +27,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/arch/dma.h>
+#include <mach/dma.h>
#include "omap-pcm.h"
static const struct snd_pcm_hardware omap_pcm_hardware = {
@@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
prtd->dma_data = dma_data;
err = omap_request_dma(dma_data->dma_req, dma_data->name,
omap_pcm_dma_irq, substream, &prtd->dma_ch);
- if (!cpu_is_omap1510()) {
+ if (!err & !cpu_is_omap1510()) {
/*
* Link channel with itself so DMA doesn't need any
* reprogramming while looping the buffer
@@ -147,12 +147,14 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
dma_params.src_start = runtime->dma_addr;
dma_params.dst_start = dma_data->port_addr;
+ dma_params.dst_port = OMAP_DMA_PORT_MPUI;
} else {
dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
dma_params.src_start = dma_data->port_addr;
dma_params.dst_start = runtime->dma_addr;
+ dma_params.src_port = OMAP_DMA_PORT_MPUI;
}
/*
* Set DMA transfer frame size equal to ALSA period size and frame
@@ -316,7 +318,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
}
-int omap_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c
new file mode 100644
index 00000000000..0fe73379689
--- /dev/null
+++ b/sound/soc/omap/osk5912.c
@@ -0,0 +1,232 @@
+/*
+ * osk5912.c -- SoC audio for OSK 5912
+ *
+ * Copyright (C) 2008 Mistral Solutions
+ *
+ * Contact: Arun KS <arunks@mistralsolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK 12000000
+
+static struct clk *tlv320aic23_mclk;
+
+static int osk_startup(struct snd_pcm_substream *substream)
+{
+ return clk_enable(tlv320aic23_mclk);
+}
+
+static void osk_shutdown(struct snd_pcm_substream *substream)
+{
+ clk_disable(tlv320aic23_mclk);
+}
+
+static int osk_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int err;
+
+ /* Set codec DAI configuration */
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (err < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return err;
+ }
+
+ /* Set cpu DAI configuration */
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (err < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return err;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ err =
+ snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
+
+ if (err < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return err;
+ }
+
+ return err;
+}
+
+static struct snd_soc_ops osk_ops = {
+ .startup = osk_startup,
+ .hw_params = osk_hw_params,
+ .shutdown = osk_shutdown,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic Jack"},
+};
+
+static int osk_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+
+ /* Add osk5912 specific widgets */
+ snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ /* Set up osk5912 specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Line In");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link osk_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &tlv320aic23_dai,
+ .init = osk_tlv320aic23_init,
+ .ops = &osk_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_machine snd_soc_machine_osk = {
+ .name = "OSK5912",
+ .dai_link = &osk_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device osk_snd_devdata = {
+ .machine = &snd_soc_machine_osk,
+ .platform = &omap_soc_platform,
+ .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *osk_snd_device;
+
+static int __init osk_soc_init(void)
+{
+ int err;
+ u32 curRate;
+ struct device *dev;
+
+ if (!(machine_is_omap_osk()))
+ return -ENODEV;
+
+ osk_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!osk_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(osk_snd_device, &osk_snd_devdata);
+ osk_snd_devdata.dev = &osk_snd_device->dev;
+ *(unsigned int *)osk_dai.cpu_dai->private_data = 0; /* McBSP1 */
+ err = platform_device_add(osk_snd_device);
+ if (err)
+ goto err1;
+
+ dev = &osk_snd_device->dev;
+
+ tlv320aic23_mclk = clk_get(dev, "mclk");
+ if (IS_ERR(tlv320aic23_mclk)) {
+ printk(KERN_ERR "Could not get mclk clock\n");
+ return -ENODEV;
+ }
+
+ if (clk_get_usecount(tlv320aic23_mclk) > 0) {
+ /* MCLK is already in use */
+ printk(KERN_WARNING
+ "MCLK in use at %d Hz. We change it to %d Hz\n",
+ (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
+ }
+
+ /*
+ * Configure 12 MHz output on MCLK.
+ */
+ curRate = (uint) clk_get_rate(tlv320aic23_mclk);
+ if (curRate != CODEC_CLOCK) {
+ if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) {
+ printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n");
+ err = -ECANCELED;
+ goto err1;
+ }
+ }
+
+ printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n",
+ (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK,
+ clk_get_usecount(tlv320aic23_mclk));
+
+ return 0;
+err1:
+ clk_put(tlv320aic23_mclk);
+ platform_device_del(osk_snd_device);
+ platform_device_put(osk_snd_device);
+
+ return err;
+
+}
+
+static void __exit osk_soc_exit(void)
+{
+ platform_device_unregister(osk_snd_device);
+}
+
+module_init(osk_soc_init);
+module_exit(osk_soc_exit);
+
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_DESCRIPTION("ALSA SoC OSK 5912");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 484f883459e..f8c1cdd940a 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,6 +1,7 @@
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
- depends on ARCH_PXA && SND_SOC
+ depends on ARCH_PXA
+ select SND_PXA2XX_LIB
help
Say Y or M if you want to add support for codecs attached to
the PXA2xx AC97, I2S or SSP interface. You will also need
@@ -13,6 +14,8 @@ config SND_PXA2XX_AC97
config SND_PXA2XX_SOC_AC97
tristate
select AC97_BUS
+ select SND_ARM
+ select SND_PXA2XX_LIB_AC97
select SND_SOC_AC97_BUS
config SND_PXA2XX_SOC_I2S
@@ -48,6 +51,7 @@ config SND_PXA2XX_SOC_POODLE
config SND_PXA2XX_SOC_TOSA
tristate "SoC AC97 Audio support for Tosa"
depends on SND_PXA2XX_SOC && MACH_TOSA
+ depends on MFD_TC6393XB
select SND_PXA2XX_SOC_AC97
select SND_SOC_WM9712
help
@@ -62,3 +66,12 @@ config SND_PXA2XX_SOC_E800
help
Say Y if you want to add support for SoC audio on the
Toshiba e800 PDA
+
+config SND_PXA2XX_SOC_EM_X270
+ tristate "SoC Audio support for CompuLab EM-x270"
+ depends on SND_PXA2XX_SOC && MACH_EM_X270
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on
+ CompuLab EM-x270.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 04e5646f75b..5bc8edf9dca 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -13,10 +13,11 @@ 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
+snd-soc-em-x270-objs := em-x270.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
-
+obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 7f32a116757..2718eaf7895 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -4,17 +4,13 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
- *
- * Revision history
- * 30th Nov 2005 Initial version.
- *
*/
#include <linux/module.h>
@@ -22,17 +18,17 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
-#include <asm/hardware/scoop.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/corgi.h>
-#include <asm/arch/audio.h>
+#include <mach/pxa-regs.h>
+#include <mach/hardware.h>
+#include <mach/corgi.h>
+#include <mach/audio.h>
#include "../codecs/wm8731.h"
#include "pxa2xx-pcm.h"
@@ -54,47 +50,51 @@ static int corgi_spk_func;
static void corgi_ext_control(struct snd_soc_codec *codec)
{
- int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
-
/* set up jack connection */
switch (corgi_jack_func) {
case CORGI_HP:
- hp = 1;
/* set = unmute headphone */
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ gpio_set_value(CORGI_GPIO_MUTE_L, 1);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 1);
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case CORGI_MIC:
- mic = 1;
/* reset = mute headphone */
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ gpio_set_value(CORGI_GPIO_MUTE_L, 0);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 0);
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case CORGI_LINE:
- line = 1;
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ gpio_set_value(CORGI_GPIO_MUTE_L, 0);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 0);
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_enable_pin(codec, "Line Jack");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case CORGI_HEADSET:
- hs = 1;
- mic = 1;
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ gpio_set_value(CORGI_GPIO_MUTE_L, 0);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 1);
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Headset Jack");
break;
}
if (corgi_spk_func == CORGI_SPK_ON)
- spk = 1;
-
- /* set the enpoints to their new connetion states */
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* signal a DAPM event */
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int corgi_startup(struct snd_pcm_substream *substream)
@@ -114,8 +114,8 @@ static int corgi_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_codec *codec = rtd->socdev->codec;
/* set = unmute headphone */
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ gpio_set_value(CORGI_GPIO_MUTE_L, 1);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 1);
return 0;
}
@@ -123,8 +123,8 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@@ -143,25 +143,25 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -218,22 +218,14 @@ static int corgi_set_spk(struct snd_kcontrol *kcontrol,
static int corgi_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (SND_SOC_DAPM_EVENT_ON(event))
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
- else
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
-
+ gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
static int corgi_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (SND_SOC_DAPM_EVENT_ON(event))
- set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
- else
- reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
-
+ gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -247,7 +239,7 @@ SND_SOC_DAPM_HP("Headset Jack", NULL),
};
/* Corgi machine audio map (connections to the codec pins) */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* headset Jack - in = micin, out = LHPOUT*/
{"Headset Jack", NULL, "LHPOUT"},
@@ -265,8 +257,6 @@ static const char *audio_map[][3] = {
/* Same as the above but no mic bias for line signals */
{"MICIN", NULL, "Line Jack"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -291,8 +281,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec)
{
int i, err;
- snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+ snd_soc_dapm_nc_pin(codec, "LLINEIN");
+ snd_soc_dapm_nc_pin(codec, "RLINEIN");
/* Add corgi specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
@@ -303,15 +293,13 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec)
}
/* Add corgi specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+ ARRAY_SIZE(wm8731_dapm_widgets));
/* Set up corgi specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -334,6 +322,7 @@ static struct snd_soc_machine snd_soc_machine_corgi = {
/* corgi audio private data */
static struct wm8731_setup_data corgi_wm8731_setup = {
+ .i2c_bus = 0,
.i2c_address = 0x1b,
};
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 06e8afb2527..6781c5be242 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -21,9 +21,9 @@
#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 <mach/pxa-regs.h>
+#include <mach/hardware.h>
+#include <mach/audio.h>
#include "../codecs/wm9712.h"
#include "pxa2xx-pcm.h"
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
new file mode 100644
index 00000000000..e6ff6929ab4
--- /dev/null
+++ b/sound/soc/pxa/em-x270.c
@@ -0,0 +1,102 @@
+/*
+ * em-x270.c -- SoC audio for EM-X270
+ *
+ * Copyright 2007 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copied from tosa.c:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/pxa-regs.h>
+#include <mach/hardware.h>
+#include <mach/audio.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_dai_link em_x270_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ },
+ {
+ .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 em_x270 = {
+ .name = "EM-X270",
+ .dai_link = em_x270_dai,
+ .num_links = ARRAY_SIZE(em_x270_dai),
+};
+
+static struct snd_soc_device em_x270_snd_devdata = {
+ .machine = &em_x270,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *em_x270_snd_device;
+
+static int __init em_x270_init(void)
+{
+ int ret;
+
+ if (!machine_is_em_x270())
+ return -ENODEV;
+
+ em_x270_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!em_x270_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(em_x270_snd_device, &em_x270_snd_devdata);
+ em_x270_snd_devdata.dev = &em_x270_snd_device->dev;
+ ret = platform_device_add(em_x270_snd_device);
+
+ if (ret)
+ platform_device_put(em_x270_snd_device);
+
+ return ret;
+}
+
+static void __exit em_x270_exit(void)
+{
+ platform_device_unregister(em_x270_snd_device);
+}
+
+module_init(em_x270_init);
+module_exit(em_x270_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 7e830b21894..4d9930c5278 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -26,10 +26,10 @@
#include <asm/mach-types.h>
#include <asm/hardware/locomo.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/poodle.h>
-#include <asm/arch/audio.h>
+#include <mach/pxa-regs.h>
+#include <mach/hardware.h>
+#include <mach/poodle.h>
+#include <mach/audio.h>
#include "../codecs/wm8731.h"
#include "pxa2xx-pcm.h"
@@ -48,8 +48,6 @@ static int poodle_spk_func;
static void poodle_ext_control(struct snd_soc_codec *codec)
{
- int spk = 0;
-
/* set up jack connection */
if (poodle_jack_func == POODLE_HP) {
/* set = unmute headphone */
@@ -57,23 +55,23 @@ static void poodle_ext_control(struct snd_soc_codec *codec)
POODLE_LOCOMO_GPIO_MUTE_L, 1);
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_R, 1);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
} else {
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_L, 0);
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_R, 0);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
}
- if (poodle_spk_func == POODLE_SPK_ON)
- spk = 1;
-
/* set the enpoints to their new connetion states */
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
+ if (poodle_spk_func == POODLE_SPK_ON)
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* signal a DAPM event */
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int poodle_startup(struct snd_pcm_substream *substream)
@@ -87,25 +85,21 @@ static int poodle_startup(struct snd_pcm_substream *substream)
}
/* we need to unmute the HP at shutdown as the mute burns power on poodle */
-static int poodle_shutdown(struct snd_pcm_substream *substream)
+static void poodle_shutdown(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
-
/* set = unmute headphone */
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_L, 1);
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_R, 1);
- return 0;
}
static int poodle_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@@ -124,25 +118,25 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -215,8 +209,8 @@ SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
};
-/* Corgi machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+/* Corgi machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to LHPOUT1, RHPOUT1 */
{"Headphone Jack", NULL, "LHPOUT"},
@@ -225,8 +219,6 @@ static const char *audio_map[][3] = {
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},
{"Ext Spk", NULL, "LOUT"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Off", "Headphone"};
@@ -236,7 +228,7 @@ static const struct soc_enum poodle_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
};
-static const snd_kcontrol_new_t wm8731_poodle_controls[] = {
+static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack,
poodle_set_jack),
SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk,
@@ -250,9 +242,9 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec)
{
int i, err;
- snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "MICIN", 1);
+ snd_soc_dapm_nc_pin(codec, "LLINEIN");
+ snd_soc_dapm_nc_pin(codec, "RLINEIN");
+ snd_soc_dapm_enable_pin(codec, "MICIN");
/* Add poodle specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
@@ -263,15 +255,13 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec)
}
/* Add poodle specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+ ARRAY_SIZE(wm8731_dapm_widgets));
/* Set up poodle specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -294,6 +284,7 @@ static struct snd_soc_machine snd_soc_machine_poodle = {
/* poodle audio private data */
static struct wm8731_setup_data poodle_wm8731_setup = {
+ .i2c_bus = 0,
.i2c_address = 0x1b,
};
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 97ec2d90547..a7a3a9c5c6f 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -13,225 +13,30 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
#include <sound/core.h>
-#include <sound/pcm.h>
#include <sound/ac97_codec.h>
-#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
-#include <asm/irq.h>
-#include <linux/mutex.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/pxa2xx-gpio.h>
-#include <asm/arch/audio.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
-static DEFINE_MUTEX(car_mutex);
-static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
-static volatile long gsr_bits;
-static struct clk *ac97_clk;
-#ifdef CONFIG_PXA27x
-static struct clk *ac97conf_clk;
-#endif
-
-/*
- * Beware PXA27x bugs:
- *
- * o Slot 12 read from modem space will hang controller.
- * o CDONE, SDONE interrupt fails after any slot 12 IO.
- *
- * We therefore have an hybrid approach for waiting on SDONE (interrupt or
- * 1 jiffy timeout if interrupt never comes).
- */
-
-static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- unsigned short val = -1;
- volatile u32 *reg_addr;
-
- mutex_lock(&car_mutex);
-
- /* set up primary or secondary codec/modem space */
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#else
- if (reg == AC97_GPIO_STATUS)
- reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
- else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#endif
- reg_addr += (reg >> 1);
-
-#ifndef CONFIG_PXA27x
- if (reg == AC97_GPIO_STATUS) {
- /* read from controller cache */
- val = *reg_addr;
- goto out;
- }
-#endif
-
- /* start read access across the ac97 link */
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- val = *reg_addr;
-
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
- if (!((GSR | gsr_bits) & GSR_SDONE)) {
- printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
- val = -1;
- goto out;
- }
-
- /* valid data now */
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- val = *reg_addr;
- /* but we've just started another cycle... */
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out: mutex_unlock(&car_mutex);
- return val;
-}
-
-static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- volatile u32 *reg_addr;
-
- mutex_lock(&car_mutex);
-
- /* set up primary or secondary codec/modem space */
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#else
- if (reg == AC97_GPIO_STATUS)
- reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
- else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#endif
- reg_addr += (reg >> 1);
-
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- *reg_addr = val;
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1);
- if (!((GSR | gsr_bits) & GSR_CDONE))
- printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
-
- mutex_unlock(&car_mutex);
-}
-
static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
-#ifdef CONFIG_PXA3xx
- int timeout = 100;
-#endif
- gsr_bits = 0;
-
-#ifdef CONFIG_PXA27x
- /* warm reset broken on Bulverde,
- so manually keep AC97 reset high */
- pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
- udelay(10);
- GCR |= GCR_WARM_RST;
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
- udelay(500);
-#elif defined(CONFIG_PXA3xx)
- /* Can't use interrupts */
- GCR |= GCR_WARM_RST;
- while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(1);
-#else
- GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
- if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
- printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
- __func__, gsr_bits);
+ pxa2xx_ac97_try_warm_reset(ac97);
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
- GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+ pxa2xx_ac97_finish_reset(ac97);
}
static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
{
-#ifdef CONFIG_PXA3xx
- int timeout = 1000;
-
- /* Hold CLKBPB for 100us */
- GCR = 0;
- GCR = GCR_CLKBPB;
- udelay(100);
- GCR = 0;
-#endif
+ pxa2xx_ac97_try_cold_reset(ac97);
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
-
- gsr_bits = 0;
-#ifdef CONFIG_PXA27x
- /* PXA27x Developers Manual section 13.5.2.2.1 */
- clk_enable(ac97conf_clk);
- udelay(5);
- clk_disable(ac97conf_clk);
- GCR = GCR_COLD_RST;
- udelay(50);
-#elif defined(CONFIG_PXA3xx)
- /* Can't use interrupts on PXA3xx */
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-
- GCR = GCR_WARM_RST | GCR_COLD_RST;
- while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(10);
-#else
- GCR = GCR_COLD_RST;
- GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
- if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
- printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
- __func__, gsr_bits);
-
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
- GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
-}
-
-static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
-{
- long status;
-
- status = GSR;
- if (status) {
- GSR = status;
- gsr_bits |= status;
- wake_up(&gsr_wq);
-
-#ifdef CONFIG_PXA27x
- /* Although we don't use those we still need to clear them
- since they tend to spuriously trigger when MMC is used
- (hardware bug? go figure)... */
- MISR = MISR_EOC;
- PISR = PISR_EOC;
- MCSR = MCSR_EOC;
-#endif
-
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
+ pxa2xx_ac97_finish_reset(ac97);
}
struct snd_ac97_bus_ops soc_ac97_ops = {
@@ -244,7 +49,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
.name = "AC97 PCM Stereo out",
.dev_addr = __PREG(PCDR),
- .drcmr = &DRCMRTXPCDR,
+ .drcmr = &DRCMR(12),
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST32 | DCMD_WIDTH4,
};
@@ -252,7 +57,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {
.name = "AC97 PCM Stereo in",
.dev_addr = __PREG(PCDR),
- .drcmr = &DRCMRRXPCDR,
+ .drcmr = &DRCMR(11),
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST32 | DCMD_WIDTH4,
};
@@ -260,7 +65,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {
.name = "AC97 Aux PCM (Slot 5) Mono out",
.dev_addr = __PREG(MODR),
- .drcmr = &DRCMRTXMODR,
+ .drcmr = &DRCMR(10),
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST16 | DCMD_WIDTH2,
};
@@ -268,7 +73,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {
.name = "AC97 Aux PCM (Slot 5) Mono in",
.dev_addr = __PREG(MODR),
- .drcmr = &DRCMRRXMODR,
+ .drcmr = &DRCMR(9),
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST16 | DCMD_WIDTH2,
};
@@ -276,33 +81,22 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
.name = "AC97 Mic PCM (Slot 6) Mono in",
.dev_addr = __PREG(MCDR),
- .drcmr = &DRCMRRXMCDR,
+ .drcmr = &DRCMR(8),
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST16 | DCMD_WIDTH2,
};
#ifdef CONFIG_PM
static int pxa2xx_ac97_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
- GCR |= GCR_ACLINK_OFF;
- clk_disable(ac97_clk);
- return 0;
+ return pxa2xx_ac97_hw_suspend();
}
static int pxa2xx_ac97_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
- pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
- pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
- pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
- pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
-#ifdef CONFIG_PXA27x
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-#endif
- clk_enable(ac97_clk);
- return 0;
+ return pxa2xx_ac97_hw_resume();
}
#else
@@ -310,69 +104,23 @@ static int pxa2xx_ac97_resume(struct platform_device *pdev,
#define pxa2xx_ac97_resume NULL
#endif
-static int pxa2xx_ac97_probe(struct platform_device *pdev)
+static int pxa2xx_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
- int ret;
-
- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
- if (ret < 0)
- goto err;
-
- pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
- pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
- pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
- pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
-#ifdef CONFIG_PXA27x
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-
- ac97conf_clk = clk_get(&pdev->dev, "AC97CONFCLK");
- if (IS_ERR(ac97conf_clk)) {
- ret = PTR_ERR(ac97conf_clk);
- ac97conf_clk = NULL;
- goto err_irq;
- }
-#endif
- ac97_clk = clk_get(&pdev->dev, "AC97CLK");
- if (IS_ERR(ac97_clk)) {
- ret = PTR_ERR(ac97_clk);
- ac97_clk = NULL;
- goto err_irq;
- }
- clk_enable(ac97_clk);
- return 0;
-
- err_irq:
- GCR |= GCR_ACLINK_OFF;
-#ifdef CONFIG_PXA27x
- if (ac97conf_clk) {
- clk_put(ac97conf_clk);
- ac97conf_clk = NULL;
- }
-#endif
- free_irq(IRQ_AC97, NULL);
- err:
- return ret;
+ return pxa2xx_ac97_hw_probe(pdev);
}
-static void pxa2xx_ac97_remove(struct platform_device *pdev)
+static void pxa2xx_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
-#ifdef CONFIG_PXA27x
- clk_put(ac97conf_clk);
- ac97conf_clk = NULL;
-#endif
- clk_disable(ac97_clk);
- clk_put(ac97_clk);
- ac97_clk = NULL;
+ pxa2xx_ac97_hw_remove(pdev);
}
static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out;
@@ -386,7 +134,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
@@ -400,7 +148,7 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -418,7 +166,7 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
* There is only 1 physical AC97 interface for pxa2xx, but it
* has extra fifo's that can be used for aux DACs and ADCs.
*/
-struct snd_soc_cpu_dai pxa_ac97_dai[] = {
+struct snd_soc_dai pxa_ac97_dai[] = {
{
.name = "pxa2xx-ac97",
.id = 0,
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
index b8ccfee095c..e390de8edcd 100644
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -14,7 +14,7 @@
#define PXA2XX_DAI_AC97_AUX 1
#define PXA2XX_DAI_AC97_MIC 2
-extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
+extern struct snd_soc_dai pxa_ac97_dai[3];
/* platform data */
extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 42507103097..e758034db5c 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -3,34 +3,82 @@
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * lrg@slimlogic.co.uk
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
- *
- * Revision history
- * 12th Aug 2005 Initial version.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/pxa2xx-gpio.h>
-#include <asm/arch/audio.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-gpio.h>
+#include <mach/audio.h>
#include "pxa2xx-pcm.h"
#include "pxa2xx-i2s.h"
+struct pxa2xx_gpio {
+ u32 sys;
+ u32 rx;
+ u32 tx;
+ u32 clk;
+ u32 frm;
+};
+
+/*
+ * I2S Controller Register and Bit Definitions
+ */
+#define SACR0 __REG(0x40400000) /* Global Control Register */
+#define SACR1 __REG(0x40400004) /* Serial Audio I 2 S/MSB-Justified Control Register */
+#define SASR0 __REG(0x4040000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
+#define SAIMR __REG(0x40400014) /* Serial Audio Interrupt Mask Register */
+#define SAICR __REG(0x40400018) /* Serial Audio Interrupt Clear Register */
+#define SADIV __REG(0x40400060) /* Audio Clock Divider Register. */
+#define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */
+
+#define SACR0_RFTH(x) ((x) << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */
+#define SACR0_TFTH(x) ((x) << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */
+#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */
+#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */
+#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */
+#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
+#define SACR0_ENB (1 << 0) /* Enable I2S Link */
+#define SACR1_ENLBF (1 << 5) /* Enable Loopback */
+#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
+#define SACR1_DREC (1 << 3) /* Disable Recording Function */
+#define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */
+
+#define SASR0_I2SOFF (1 << 7) /* Controller Status */
+#define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */
+#define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */
+#define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */
+#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */
+#define SASR0_BSY (1 << 2) /* I2S Busy */
+#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */
+#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
+
+#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */
+#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */
+
+#define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */
+#define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */
+#define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */
+#define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */
+
struct pxa_i2s_port {
u32 sadiv;
u32 sacr0;
@@ -40,11 +88,12 @@ struct pxa_i2s_port {
u32 fmt;
};
static struct pxa_i2s_port pxa_i2s;
+static struct clk *clk_i2s;
static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
.name = "I2S PCM Stereo out",
.dev_addr = __PREG(SADR),
- .drcmr = &DRCMRTXSADR,
+ .drcmr = &DRCMR(3),
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST32 | DCMD_WIDTH4,
};
@@ -52,7 +101,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
.name = "I2S PCM Stereo in",
.dev_addr = __PREG(SADR),
- .drcmr = &DRCMRRXSADR,
+ .drcmr = &DRCMR(2),
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST32 | DCMD_WIDTH4,
};
@@ -65,11 +114,6 @@ static struct pxa2xx_gpio gpio_bus[] = {
.frm = GPIO31_SYNC_I2S_MD,
},
{ /* I2S SoC Master */
-#ifdef CONFIG_PXA27x
- .sys = GPIO113_I2S_SYSCLK_MD,
-#else
- .sys = GPIO32_SYSCLK_I2S_MD,
-#endif
.rx = GPIO29_SDATA_IN_I2S_MD,
.tx = GPIO30_SDATA_OUT_I2S_MD,
.clk = GPIO28_BITCLK_OUT_I2S_MD,
@@ -80,7 +124,10 @@ static struct pxa2xx_gpio gpio_bus[] = {
static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (IS_ERR(clk_i2s))
+ return PTR_ERR(clk_i2s);
if (!cpu_dai->active) {
SACR0 |= SACR0_RST;
@@ -101,7 +148,7 @@ static int pxa_i2s_wait(void)
return 0;
}
-static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
/* interface format */
@@ -127,7 +174,7 @@ static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
return 0;
}
-static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
if (clk_id != PXA2XX_I2S_SYSCLK)
@@ -143,13 +190,14 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
- pxa_set_cken(CKEN_I2S, 1);
+ BUG_ON(IS_ERR(clk_i2s));
+ clk_enable(clk_i2s);
pxa_i2s_wait();
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -234,13 +282,15 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
SACR0 &= ~SACR0_ENB;
pxa_i2s_wait();
- pxa_set_cken(CKEN_I2S, 0);
+ clk_disable(clk_i2s);
}
+
+ clk_put(clk_i2s);
}
#ifdef CONFIG_PM
static int pxa2xx_i2s_suspend(struct platform_device *dev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
@@ -258,7 +308,7 @@ static int pxa2xx_i2s_suspend(struct platform_device *dev,
}
static int pxa2xx_i2s_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
@@ -283,7 +333,7 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
-struct snd_soc_cpu_dai pxa_i2s_dai = {
+struct snd_soc_dai pxa_i2s_dai = {
.name = "pxa2xx-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
@@ -312,7 +362,49 @@ struct snd_soc_cpu_dai pxa_i2s_dai = {
EXPORT_SYMBOL_GPL(pxa_i2s_dai);
+static int pxa2xx_i2s_probe(struct platform_device *dev)
+{
+ clk_i2s = clk_get(&dev->dev, "I2SCLK");
+ return IS_ERR(clk_i2s) ? PTR_ERR(clk_i2s) : 0;
+}
+
+static int __devexit pxa2xx_i2s_remove(struct platform_device *dev)
+{
+ clk_put(clk_i2s);
+ clk_i2s = ERR_PTR(-ENOENT);
+ return 0;
+}
+
+static struct platform_driver pxa2xx_i2s_driver = {
+ .probe = pxa2xx_i2s_probe,
+ .remove = __devexit_p(pxa2xx_i2s_remove),
+
+ .driver = {
+ .name = "pxa2xx-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pxa2xx_i2s_init(void)
+{
+ if (cpu_is_pxa27x())
+ gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD;
+ else
+ gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD;
+
+ clk_i2s = ERR_PTR(-ENOENT);
+ return platform_driver_register(&pxa2xx_i2s_driver);
+}
+
+static void __exit pxa2xx_i2s_exit(void)
+{
+ platform_driver_unregister(&pxa2xx_i2s_driver);
+}
+
+module_init(pxa2xx_i2s_init);
+module_exit(pxa2xx_i2s_exit);
+
/* Module information */
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h
index 4435bd9f884..e2def441153 100644
--- a/sound/soc/pxa/pxa2xx-i2s.h
+++ b/sound/soc/pxa/pxa2xx-i2s.h
@@ -15,6 +15,6 @@
/* I2S clock */
#define PXA2XX_I2S_SYSCLK 0
-extern struct snd_soc_cpu_dai pxa_i2s_dai;
+extern struct snd_soc_dai pxa_i2s_dai;
#endif
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 01ad7bf716b..afcd892cd2f 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -10,64 +10,14 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
-
-#include <asm/dma.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/audio.h>
+#include <sound/pxa2xx-lib.h>
#include "pxa2xx-pcm.h"
-
-static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
- .period_bytes_min = 32,
- .period_bytes_max = 8192 - 32,
- .periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
- .buffer_bytes_max = 128 * 1024,
- .fifo_size = 32,
-};
-
-struct pxa2xx_runtime_data {
- int dma_ch;
- struct pxa2xx_pcm_dma_params *params;
- pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
-};
-
-static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- int dcsr;
-
- dcsr = DCSR(dma_ch);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- if (dcsr & DCSR_ENDINTR) {
- snd_pcm_period_elapsed(substream);
- } else {
- printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
- prtd->params->name, dma_ch, dcsr);
- }
-}
+#include "../../arm/pxa2xx-pcm.h"
static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -76,10 +26,6 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct pxa2xx_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
- size_t totsize = params_buffer_bytes(params);
- size_t period = params_period_bytes(params);
- pxa_dma_desc *dma_desc;
- dma_addr_t dma_buff_phys, next_desc_phys;
int ret;
/* return if this is a bufferless transfer e.g.
@@ -106,42 +52,16 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
prtd->dma_ch = ret;
}
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
-
- dma_desc = prtd->dma_desc_array;
- next_desc_phys = prtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- dma_desc->ddadr = next_desc_phys;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = prtd->params->dev_addr;
- } else {
- dma_desc->dsadr = prtd->params->dev_addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = prtd->dma_desc_array_phys;
-
- return 0;
+ return __pxa2xx_pcm_hw_params(substream, params);
}
static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- if (prtd && prtd->params)
- *prtd->params->drcmr = 0;
+ __pxa2xx_pcm_hw_free(substream);
if (prtd->dma_ch) {
- snd_pcm_set_runtime_buffer(substream, NULL);
pxa_free_dma(prtd->dma_ch);
prtd->dma_ch = 0;
}
@@ -149,188 +69,21 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- DCSR(prtd->dma_ch) = 0;
- DCMD(prtd->dma_ch) = 0;
- *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
-
- return 0;
-}
-
-static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static snd_pcm_uframes_t
-pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
- if (x == runtime->buffer_size)
- x = 0;
- return x;
-}
-
-static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd;
- int ret;
-
- snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware);
-
- /*
- * For mysterious reasons (and despite what the manual says)
- * playback samples are lost if the DMA count is not a multiple
- * of the DMA burst size. Let's add a rule to enforce that.
- */
- ret = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
- if (ret)
- goto out;
-
- ret = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
- if (ret)
- goto out;
-
- ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL);
- if (prtd == NULL) {
- ret = -ENOMEM;
- goto out;
- }
-
- prtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &prtd->dma_desc_array_phys, GFP_KERNEL);
- if (!prtd->dma_desc_array) {
- ret = -ENOMEM;
- goto err1;
- }
-
- runtime->private_data = prtd;
- return 0;
-
- err1:
- kfree(prtd);
- out:
- return ret;
-}
-
-static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- prtd->dma_desc_array, prtd->dma_desc_array_phys);
- kfree(prtd);
- return 0;
-}
-
-static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
struct snd_pcm_ops pxa2xx_pcm_ops = {
- .open = pxa2xx_pcm_open,
- .close = pxa2xx_pcm_close,
+ .open = __pxa2xx_pcm_open,
+ .close = __pxa2xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pxa2xx_pcm_hw_params,
.hw_free = pxa2xx_pcm_hw_free,
- .prepare = pxa2xx_pcm_prepare,
+ .prepare = __pxa2xx_pcm_prepare,
.trigger = pxa2xx_pcm_trigger,
.pointer = pxa2xx_pcm_pointer,
.mmap = pxa2xx_pcm_mmap,
};
-static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
-}
-
-static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
-int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
@@ -360,7 +113,7 @@ int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
struct snd_soc_platform pxa2xx_soc_platform = {
.name = "pxa2xx-audio",
.pcm_ops = &pxa2xx_pcm_ops,
- .pcm_new = pxa2xx_pcm_new,
+ .pcm_new = pxa2xx_soc_pcm_new,
.pcm_free = pxa2xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h
index 54c9c755e50..60c3b20aeeb 100644
--- a/sound/soc/pxa/pxa2xx-pcm.h
+++ b/sound/soc/pxa/pxa2xx-pcm.h
@@ -13,21 +13,6 @@
#ifndef _PXA2XX_PCM_H
#define _PXA2XX_PCM_H
-struct pxa2xx_pcm_dma_params {
- char *name; /* stream identifier */
- u32 dcmd; /* DMA descriptor dcmd field */
- volatile u32 *drcmr; /* the DMA request channel to use */
- u32 dev_addr; /* device physical address for DMA */
-};
-
-struct pxa2xx_gpio {
- u32 sys;
- u32 rx;
- u32 tx;
- u32 clk;
- u32 frm;
-};
-
/* platform data */
extern struct snd_soc_platform pxa2xx_soc_platform;
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index d8b8372db00..d307b6757e9 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -12,9 +12,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 30th Nov 2005 Initial version.
- *
*/
#include <linux/module.h>
@@ -22,17 +19,16 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
-#include <asm/hardware/scoop.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/akita.h>
-#include <asm/arch/spitz.h>
+#include <mach/pxa-regs.h>
+#include <mach/hardware.h>
+#include <mach/spitz.h>
#include "../codecs/wm8750.h"
#include "pxa2xx-pcm.h"
#include "pxa2xx-i2s.h"
@@ -54,60 +50,60 @@ static int spitz_spk_func;
static void spitz_ext_control(struct snd_soc_codec *codec)
{
if (spitz_spk_func == SPITZ_SPK_ON)
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
else
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0);
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* set up jack connection */
switch (spitz_jack_func) {
case SPITZ_HP:
/* enable and unmute hp jack, disable mic bias */
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
- set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
- set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 1);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
break;
case SPITZ_MIC:
/* enable mic jack and bias, mute hp */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
break;
case SPITZ_LINE:
/* enable line jack, disable mic bias and mute hp */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 1);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_enable_pin(codec, "Line Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
break;
case SPITZ_HEADSET:
/* enable and unmute headset jack enable mic bias, mute L hp */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
- set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Headset Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
break;
case SPITZ_HP_OFF:
/* jack removed, everything off */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
- reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
break;
}
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int spitz_startup(struct snd_pcm_substream *substream)
@@ -124,8 +120,8 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@@ -144,25 +140,25 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -218,23 +214,14 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol,
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (machine_is_borzoi() || machine_is_spitz()) {
- if (SND_SOC_DAPM_EVENT_ON(event))
- set_scoop_gpio(&spitzscoop2_device.dev,
- SPITZ_SCP2_MIC_BIAS);
- else
- reset_scoop_gpio(&spitzscoop2_device.dev,
- SPITZ_SCP2_MIC_BIAS);
- }
+ if (machine_is_borzoi() || machine_is_spitz())
+ gpio_set_value(SPITZ_GPIO_MIC_BIAS,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ if (machine_is_akita())
+ gpio_set_value(AKITA_GPIO_MIC_BIAS,
+ SND_SOC_DAPM_EVENT_ON(event));
- if (machine_is_akita()) {
- if (SND_SOC_DAPM_EVENT_ON(event))
- akita_set_ioexp(&akitaioexp_device.dev,
- AKITA_IOEXP_MIC_BIAS);
- else
- akita_reset_ioexp(&akitaioexp_device.dev,
- AKITA_IOEXP_MIC_BIAS);
- }
return 0;
}
@@ -250,7 +237,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
};
/* Spitz machine audio_map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to LOUT1, ROUT1 */
{"Headphone Jack", NULL, "LOUT1"},
@@ -269,8 +256,6 @@ static const char *audio_map[][3] = {
/* line is connected to input 1 - no bias */
{"LINPUT1", NULL, "Line Jack"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -296,13 +281,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec)
int i, err;
/* NC codec pins */
- snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0);
- snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "MONO", 0);
+ snd_soc_dapm_nc_pin(codec, "RINPUT1");
+ snd_soc_dapm_nc_pin(codec, "LINPUT2");
+ snd_soc_dapm_nc_pin(codec, "RINPUT2");
+ snd_soc_dapm_nc_pin(codec, "LINPUT3");
+ snd_soc_dapm_nc_pin(codec, "RINPUT3");
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "MONO1");
/* Add spitz specific controls */
for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
@@ -313,15 +298,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec)
}
/* Add spitz specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+ ARRAY_SIZE(wm8750_dapm_widgets));
- /* Set up spitz specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ /* Set up spitz specific audio paths */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -344,6 +327,7 @@ static struct snd_soc_machine snd_soc_machine_spitz = {
/* spitz audio private data */
static struct wm8750_setup_data spitz_wm8750_setup = {
+ .i2c_bus = 0,
.i2c_address = 0x1b,
};
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index 7346d7e5d06..afefe41b8c4 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -12,9 +12,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 30th Nov 2005 Initial version.
- *
* GPIO's
* 1 - Jack Insertion
* 5 - Hookswitch (headset answer/hang up switch)
@@ -24,6 +21,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
+#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -31,11 +29,10 @@
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
-#include <asm/hardware/tmio.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/audio.h>
-#include <asm/arch/tosa.h>
+#include <mach/tosa.h>
+#include <mach/pxa-regs.h>
+#include <mach/hardware.h>
+#include <mach/audio.h>
#include "../codecs/wm9712.h"
#include "pxa2xx-pcm.h"
@@ -55,29 +52,31 @@ static int tosa_spk_func;
static void tosa_ext_control(struct snd_soc_codec *codec)
{
- int spk = 0, mic_int = 0, hp = 0, hs = 0;
-
/* set up jack connection */
switch (tosa_jack_func) {
case TOSA_HP:
- hp = 1;
+ snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case TOSA_MIC_INT:
- mic_int = 1;
+ snd_soc_dapm_enable_pin(codec, "Mic (Internal)");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case TOSA_HEADSET:
- hs = 1;
+ snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Headset Jack");
break;
}
if (tosa_spk_func == TOSA_SPK_ON)
- spk = 1;
+ snd_soc_dapm_enable_pin(codec, "Speaker");
+ else
+ snd_soc_dapm_disable_pin(codec, "Speaker");
- snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
- snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int tosa_startup(struct snd_pcm_substream *substream)
@@ -138,10 +137,7 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol,
static int tosa_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (SND_SOC_DAPM_EVENT_ON(event))
- set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
- else
- reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
+ gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
return 0;
}
@@ -154,7 +150,7 @@ SND_SOC_DAPM_SPK("Speaker", NULL),
};
/* tosa audio map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to HPOUTL, HPOUTR */
{"Headphone Jack", NULL, "HPOUTL"},
@@ -173,8 +169,6 @@ static const char *audio_map[][3] = {
{"Headset Jack", NULL, "HPOUTR"},
{"LINEINR", NULL, "Mic Bias"},
{"Mic Bias", NULL, "Headset Jack"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -196,8 +190,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec)
{
int i, err;
- snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "MONOOUT");
/* add tosa specific controls */
for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
@@ -208,17 +202,13 @@ static int tosa_ac97_init(struct snd_soc_codec *codec)
}
/* add tosa specific widgets */
- for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
- snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
- }
+ snd_soc_dapm_new_controls(codec, tosa_dapm_widgets,
+ ARRAY_SIZE(tosa_dapm_widgets));
/* set up tosa specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -261,16 +251,28 @@ static int __init tosa_init(void)
if (!machine_is_tosa())
return -ENODEV;
+ ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
+ if (ret)
+ return ret;
+ gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
+
tosa_snd_device = platform_device_alloc("soc-audio", -1);
- if (!tosa_snd_device)
- return -ENOMEM;
+ if (!tosa_snd_device) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata);
tosa_snd_devdata.dev = &tosa_snd_device->dev;
ret = platform_device_add(tosa_snd_device);
- if (ret)
- platform_device_put(tosa_snd_device);
+ if (!ret)
+ return 0;
+
+ platform_device_put(tosa_snd_device);
+
+err_alloc:
+ gpio_free(TOSA_GPIO_L_MUTE);
return ret;
}
@@ -278,6 +280,7 @@ static int __init tosa_init(void)
static void __exit tosa_exit(void)
{
platform_device_unregister(tosa_snd_device);
+ gpio_free(TOSA_GPIO_L_MUTE);
}
module_init(tosa_init);
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 1f6dbfc4caa..b9f2353effe 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,7 +1,6 @@
config SND_S3C24XX_SOC
tristate "SoC Audio for the Samsung S3C24XX chips"
- depends on ARCH_S3C2410 && SND_SOC
- select SND_PCM
+ depends on ARCH_S3C2410
help
Say Y or M if you want to add support for codecs attached to
the S3C24XX AC97, I2S or SSP interface. You will also need
@@ -16,7 +15,6 @@ config SND_S3C2412_SOC_I2S
config SND_S3C2443_SOC_AC97
tristate
select AC97_BUS
- select SND_AC97_CODEC
select SND_SOC_AC97_BUS
config SND_S3C24XX_SOC_NEO1973_WM8753
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 0e9d1c5f248..87ddfefcc2f 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -10,10 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 20th Jan 2007 Initial version.
- * 05th Feb 2007 Rename all to Neo1973
- *
*/
#include <linux/module.h>
@@ -26,15 +22,16 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
#include <asm/mach-types.h>
#include <asm/hardware/scoop.h>
-#include <asm/arch/regs-clock.h>
-#include <asm/arch/regs-gpio.h>
-#include <asm/hardware.h>
-#include <asm/arch/audio.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+#include <mach/hardware.h>
+#include <mach/audio.h>
#include <linux/io.h>
-#include <asm/arch/spi-gpio.h>
+#include <mach/spi-gpio.h>
#include <asm/plat-s3c24xx/regs-iis.h>
@@ -43,6 +40,14 @@
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
+/* Debugging stuff */
+#define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0
+#if S3C24XX_SOC_NEO1973_WM8753_DEBUG
+#define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x)
+#else
+#define DBG(x...)
+#endif
+
/* define the scenarios */
#define NEO_AUDIO_OFF 0
#define NEO_GSM_CALL_AUDIO_HANDSET 1
@@ -61,12 +66,14 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int pll_out = 0, bclk = 0;
int ret = 0;
unsigned long iis_clkrate;
+ DBG("Entered %s\n", __func__);
+
iis_clkrate = s3c24xx_i2s_get_clockrate();
switch (params_rate(params)) {
@@ -101,44 +108,44 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai,
+ ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+ ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set MCLK division for sample rate */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
S3C2410_IISMOD_32FS);
if (ret < 0)
return ret;
/* set codec BCLK division for sample rate */
- ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
if (ret < 0)
return ret;
/* set prescaler division for sample rate */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
S3C24XX_PRESCALE(4, 4));
if (ret < 0)
return ret;
/* codec PLL input is PCLK/4 */
- ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
@@ -149,10 +156,12 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
static int neo1973_hifi_hw_free(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_dai *codec_dai = rtd->dai->codec_dai;
+
+ DBG("Entered %s\n", __func__);
/* disable the PLL */
- return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
}
/*
@@ -167,11 +176,13 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
unsigned int pcmdiv = 0;
int ret = 0;
unsigned long iis_clkrate;
+ DBG("Entered %s\n", __func__);
+
iis_clkrate = s3c24xx_i2s_get_clockrate();
if (params_rate(params) != 8000)
@@ -183,24 +194,24 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
/* todo: gg check mode (DSP_B) against CSR datasheet */
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set codec PCM division for sample rate */
- ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
if (ret < 0)
return ret;
/* configue and enable PLL for 12.288MHz output */
- ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
@@ -211,10 +222,12 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
static int neo1973_voice_hw_free(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_dai *codec_dai = rtd->dai->codec_dai;
+
+ DBG("Entered %s\n", __func__);
/* disable the PLL */
- return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
}
static struct snd_soc_ops neo1973_voice_ops = {
@@ -233,79 +246,81 @@ static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
{
+ DBG("Entered %s\n", __func__);
+
switch (neo1973_scenario) {
case NEO_AUDIO_OFF:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_GSM_CALL_AUDIO_HANDSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_enable_pin(codec, "Call Mic");
break;
case NEO_GSM_CALL_AUDIO_HEADSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_enable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_GSM_CALL_AUDIO_BLUETOOTH:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_STEREO_TO_SPEAKERS:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_STEREO_TO_HEADPHONES:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_CAPTURE_HANDSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_enable_pin(codec, "Call Mic");
break;
case NEO_CAPTURE_HEADSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_enable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_CAPTURE_BLUETOOTH:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
default:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
}
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -315,6 +330,8 @@ static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ DBG("Entered %s\n", __func__);
+
if (neo1973_scenario == ucontrol->value.integer.value[0])
return 0;
@@ -327,6 +344,8 @@ static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
static void lm4857_write_regs(void)
{
+ DBG("Entered %s\n", __func__);
+
if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
printk(KERN_ERR "lm4857: i2c write failed\n");
}
@@ -338,6 +357,8 @@ static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
int shift = (kcontrol->private_value >> 8) & 0x0F;
int mask = (kcontrol->private_value >> 16) & 0xFF;
+ DBG("Entered %s\n", __func__);
+
ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
return 0;
}
@@ -364,6 +385,8 @@ static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
{
u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
+ DBG("Entered %s\n", __func__);
+
if (value)
value -= 5;
@@ -376,6 +399,8 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
{
u8 value = ucontrol->value.integer.value[0];
+ DBG("Entered %s\n", __func__);
+
if (value)
value += 5;
@@ -397,8 +422,7 @@ static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
};
-/* example machine audio_mapnections */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route dapm_routes[] = {
/* Connections to the lm4857 amp */
{"Audio Out", NULL, "LOUT1"},
@@ -421,8 +445,6 @@ static const char *audio_map[][3] = {
/* Connect the ALC pins */
{"ACIN", NULL, "ACOP"},
-
- {NULL, NULL, NULL},
};
static const char *lm4857_mode[] = {
@@ -453,13 +475,16 @@ static const struct soc_enum neo_scenario_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios),
};
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
- SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
- lm4857_get_reg, lm4857_set_reg),
- SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
- lm4857_get_reg, lm4857_set_reg),
- SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
- lm4857_get_reg, lm4857_set_reg),
+ SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+ lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+ SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+ lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+ SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+ lm4857_get_reg, lm4857_set_reg, mono_tlv),
SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
lm4857_get_mode, lm4857_set_mode),
SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
@@ -483,22 +508,23 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
{
int i, err;
+ DBG("Entered %s\n", __func__);
+
/* set up NC codec pins */
- snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
- snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
- snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
+ snd_soc_dapm_nc_pin(codec, "LOUT2");
+ snd_soc_dapm_nc_pin(codec, "ROUT2");
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "OUT4");
+ snd_soc_dapm_nc_pin(codec, "LINE1");
+ snd_soc_dapm_nc_pin(codec, "LINE2");
+ /* Add neo1973 specific widgets */
+ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+ ARRAY_SIZE(wm8753_dapm_widgets));
/* set endpoints to default mode */
set_scenario_endpoints(codec, NEO_AUDIO_OFF);
- /* Add neo1973 specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
-
/* add neo1973 specific controls */
for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
err = snd_ctl_add(codec->card,
@@ -508,20 +534,18 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
return err;
}
- /* set up neo1973 specific audio path audio_mapnects */
- for (i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
+ /* set up neo1973 specific audio routes */
+ err = snd_soc_dapm_add_routes(codec, dapm_routes,
+ ARRAY_SIZE(dapm_routes));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
/*
* BT Codec DAI
*/
-static struct snd_soc_cpu_dai bt_dai = {
+static struct snd_soc_dai bt_dai = {
.name = "Bluetooth",
.id = 0,
.type = SND_SOC_DAI_PCM,
@@ -562,6 +586,7 @@ static struct snd_soc_machine neo1973 = {
};
static struct wm8753_setup_data neo1973_wm8753_setup = {
+ .i2c_bus = 0,
.i2c_address = 0x1a,
};
@@ -572,54 +597,32 @@ static struct snd_soc_device neo1973_snd_devdata = {
.codec_data = &neo1973_wm8753_setup,
};
-static struct i2c_client client_template;
-
-static const unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
+static int lm4857_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- int ret;
+ DBG("Entered %s\n", __func__);
- client_template.adapter = adap;
- client_template.addr = addr;
-
- i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
- if (i2c == NULL)
- return -ENOMEM;
-
- ret = i2c_attach_client(i2c);
- if (ret < 0) {
- printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr);
- goto exit_err;
- }
+ i2c = client;
lm4857_write_regs();
- return ret;
-
-exit_err:
- kfree(i2c);
- return ret;
-}
-
-static int lm4857_i2c_detach(struct i2c_client *client)
-{
- i2c_detach_client(client);
- kfree(client);
return 0;
}
-static int lm4857_i2c_attach(struct i2c_adapter *adap)
+static int lm4857_i2c_remove(struct i2c_client *client)
{
- return i2c_probe(adap, &addr_data, lm4857_amp_probe);
+ DBG("Entered %s\n", __func__);
+
+ i2c = NULL;
+
+ return 0;
}
static u8 lm4857_state;
static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
{
+ DBG("Entered %s\n", __func__);
+
dev_dbg(&dev->dev, "lm4857_suspend\n");
lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
if (lm4857_state) {
@@ -631,6 +634,8 @@ static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
static int lm4857_resume(struct i2c_client *dev)
{
+ DBG("Entered %s\n", __func__);
+
if (lm4857_state) {
lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
lm4857_write_regs();
@@ -640,29 +645,29 @@ static int lm4857_resume(struct i2c_client *dev)
static void lm4857_shutdown(struct i2c_client *dev)
{
+ DBG("Entered %s\n", __func__);
+
dev_dbg(&dev->dev, "lm4857_shutdown\n");
lm4857_regs[LM4857_CTRL] &= 0xf0;
lm4857_write_regs();
}
-/* corgi i2c codec control layer */
+static const struct i2c_device_id lm4857_i2c_id[] = {
+ { "neo1973_lm4857", 0 },
+ { }
+};
+
static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "LM4857 I2C Amp",
.owner = THIS_MODULE,
},
- .id = I2C_DRIVERID_LM4857,
.suspend = lm4857_suspend,
.resume = lm4857_resume,
.shutdown = lm4857_shutdown,
- .attach_adapter = lm4857_i2c_attach,
- .detach_client = lm4857_i2c_detach,
- .command = NULL,
-};
-
-static struct i2c_client client_template = {
- .name = "LM4857",
- .driver = &lm4857_i2c_driver,
+ .probe = lm4857_i2c_probe,
+ .remove = lm4857_i2c_remove,
+ .id_table = lm4857_i2c_id,
};
static struct platform_device *neo1973_snd_device;
@@ -671,6 +676,14 @@ static int __init neo1973_init(void)
{
int ret;
+ DBG("Entered %s\n", __func__);
+
+ if (!machine_is_neo1973_gta01()) {
+ printk(KERN_INFO
+ "Only GTA01 hardware supported by ASoC driver\n");
+ return -ENODEV;
+ }
+
neo1973_snd_device = platform_device_alloc("soc-audio", -1);
if (!neo1973_snd_device)
return -ENOMEM;
@@ -679,18 +692,23 @@ static int __init neo1973_init(void)
neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
ret = platform_device_add(neo1973_snd_device);
- if (ret)
+ if (ret) {
platform_device_put(neo1973_snd_device);
+ return ret;
+ }
ret = i2c_add_driver(&lm4857_i2c_driver);
+
if (ret != 0)
- printk(KERN_ERR "can't add i2c driver");
+ platform_device_unregister(neo1973_snd_device);
return ret;
}
static void __exit neo1973_exit(void)
{
+ DBG("Entered %s\n", __func__);
+
i2c_del_driver(&lm4857_i2c_driver);
platform_device_unregister(neo1973_snd_device);
}
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index c4a46dd589b..ded7d995a92 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -28,16 +28,16 @@
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include <asm/hardware.h>
+#include <mach/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 <mach/regs-gpio.h>
+#include <mach/audio.h>
+#include <mach/dma.h>
#include "s3c24xx-pcm.h"
#include "s3c2412-i2s.h"
@@ -295,7 +295,7 @@ static inline int s3c2412_snd_is_clkmaster(void)
/*
* Set S3C2412 I2S DAI format
*/
-static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
u32 iismod;
@@ -500,7 +500,7 @@ EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
/*
* Set S3C2412 Clock source
*/
-static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
@@ -528,7 +528,7 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
/*
* Set S3C2412 Clock dividers
*/
-static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
@@ -601,7 +601,8 @@ struct clk *s3c2412_get_iisclk(void)
EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
-static int s3c2412_i2s_probe(struct platform_device *pdev)
+static int s3c2412_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
DBG("Entered %s\n", __func__);
@@ -647,7 +648,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
static int s3c2412_i2s_suspend(struct platform_device *dev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
u32 iismod;
@@ -675,7 +676,7 @@ static int s3c2412_i2s_suspend(struct platform_device *dev,
}
static int s3c2412_i2s_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
@@ -707,7 +708,7 @@ static int s3c2412_i2s_resume(struct platform_device *pdev,
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 = {
+struct snd_soc_dai s3c2412_i2s_dai = {
.name = "s3c2412-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index 27f48e1ffa8..aac08a25e54 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -24,7 +24,7 @@
extern struct clk *s3c2412_get_iisclk(void);
-extern struct snd_soc_cpu_dai s3c2412_i2s_dai;
+extern struct snd_soc_dai s3c2412_i2s_dai;
struct s3c2412_rate_calc {
unsigned int clk_div; /* for prescaler */
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index e81d9a6c83d..19c5c3cf5d8 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -10,9 +10,6 @@
* 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.
- *
- * Revision history
- * 21st Mar 2007 Initial Version
*/
#include <linux/init.h>
@@ -30,13 +27,13 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <asm/hardware.h>
+#include <mach/hardware.h>
#include <asm/plat-s3c/regs-ac97.h>
-#include <asm/arch/regs-gpio.h>
-#include <asm/arch/regs-clock.h>
-#include <asm/arch/audio.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-clock.h>
+#include <mach/audio.h>
#include <asm/dma.h>
-#include <asm/arch/dma.h>
+#include <mach/dma.h>
#include "s3c24xx-pcm.h"
#include "s3c24xx-ac97.h"
@@ -212,7 +209,8 @@ static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
.dma_size = 4,
};
-static int s3c2443_ac97_probe(struct platform_device *pdev)
+static int s3c2443_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
int ret;
u32 ac_glbctrl;
@@ -263,7 +261,8 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
return ret;
}
-static void s3c2443_ac97_remove(struct platform_device *pdev)
+static void s3c2443_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
free_irq(IRQ_S3C244x_AC97, NULL);
clk_disable(s3c24xx_ac97.ac97_clk);
@@ -275,7 +274,7 @@ static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
@@ -317,7 +316,7 @@ static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -353,7 +352,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
+struct snd_soc_dai s3c2443_ac97_dai[] = {
{
.name = "s3c2443-ac97",
.id = 0,
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
index bf03e8ed16c..a96dcadf28b 100644
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
@@ -26,6 +26,6 @@
#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
#endif
-extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+extern struct snd_soc_dai s3c2443_ac97_dai[];
#endif /*S3C24XXAC97_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 1ed6afd4545..ba4476b55fb 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -12,11 +12,6 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
- *
- *
- * Revision history
- * 11th Dec 2006 Merged with Simtec driver
- * 10th Nov 2006 Initial version.
*/
#include <linux/init.h>
@@ -32,12 +27,12 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <asm/hardware.h>
-#include <asm/arch/regs-gpio.h>
-#include <asm/arch/regs-clock.h>
-#include <asm/arch/audio.h>
+#include <mach/hardware.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-clock.h>
+#include <mach/audio.h>
#include <asm/dma.h>
-#include <asm/arch/dma.h>
+#include <mach/dma.h>
#include <asm/plat-s3c24xx/regs-iis.h>
@@ -180,7 +175,7 @@ static void s3c24xx_snd_rxctrl(int on)
static int s3c24xx_snd_lrsync(void)
{
u32 iiscon;
- unsigned long timeout = jiffies + msecs_to_jiffies(5);
+ int timeout = 50; /* 5ms */
DBG("Entered %s\n", __func__);
@@ -189,8 +184,9 @@ static int s3c24xx_snd_lrsync(void)
if (iiscon & S3C2410_IISCON_LRINDEX)
break;
- if (time_after(jiffies, timeout))
+ if (!timeout--)
return -ETIMEDOUT;
+ udelay(100);
}
return 0;
@@ -209,7 +205,7 @@ static inline int s3c24xx_snd_is_clkmaster(void)
/*
* Set S3C24xx I2S DAI format
*/
-static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
u32 iismod;
@@ -317,7 +313,7 @@ exit_err:
/*
* Set S3C24xx Clock source
*/
-static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -343,7 +339,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
/*
* Set S3C24xx Clock dividers
*/
-static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
u32 reg;
@@ -381,7 +377,8 @@ u32 s3c24xx_i2s_get_clockrate(void)
}
EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
-static int s3c24xx_i2s_probe(struct platform_device *pdev)
+static int s3c24xx_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
DBG("Entered %s\n", __func__);
@@ -414,7 +411,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
static int s3c24xx_i2s_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
DBG("Entered %s\n", __func__);
@@ -429,7 +426,7 @@ static int s3c24xx_i2s_suspend(struct platform_device *pdev,
}
static int s3c24xx_i2s_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
DBG("Entered %s\n", __func__);
clk_enable(s3c24xx_i2s.iis_clk);
@@ -452,7 +449,7 @@ static int s3c24xx_i2s_resume(struct platform_device *pdev,
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 s3c24xx_i2s_dai = {
+struct snd_soc_dai s3c24xx_i2s_dai = {
.name = "s3c24xx-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/s3c24xx/s3c24xx-i2s.h
index 537b4ecce8a..726d91cf4e1 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.h
@@ -32,6 +32,6 @@
u32 s3c24xx_i2s_get_clockrate(void);
-extern struct snd_soc_cpu_dai s3c24xx_i2s_dai;
+extern struct snd_soc_dai s3c24xx_i2s_dai;
#endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 7806ae61461..e13e614bada 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -12,10 +12,6 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
- *
- * Revision history
- * 11th Dec 2006 Merged with Simtec driver
- * 10th Nov 2006 Initial version.
*/
#include <linux/module.h>
@@ -31,9 +27,9 @@
#include <sound/soc.h>
#include <asm/dma.h>
-#include <asm/hardware.h>
-#include <asm/arch/dma.h>
-#include <asm/arch/audio.h>
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/audio.h>
#include "s3c24xx-pcm.h"
@@ -433,7 +429,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;
static int s3c24xx_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret = 0;
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index b4a56302b9a..8515d6ff03f 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -10,9 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 8th Mar 2007 Initial version.
- *
*/
#include <linux/module.h>
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 4c1e013381c..54bd604012a 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -3,7 +3,7 @@ menu "SoC Audio support for SuperH"
config SND_SOC_PCM_SH7760
tristate "SoC Audio support for Renesas SH7760"
- depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
+ depends on CPU_SUBTYPE_SH7760 && SH_DMABRG
help
Enable this option for SH7760 AC97/I2S audio support.
@@ -13,10 +13,9 @@ config SND_SOC_PCM_SH7760
##
config SND_SOC_SH4_HAC
+ tristate
select AC97_BUS
select SND_SOC_AC97_BUS
- select SND_AC97_CODEC
- tristate
config SND_SOC_SH4_SSI
tristate
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index 7a3ce80d672..9faa12622d0 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -326,7 +326,7 @@ static void camelot_pcm_free(struct snd_pcm *pcm)
}
static int camelot_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai,
+ struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index b7b676b3d67..df7bc345c32 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -266,7 +266,7 @@ static int hac_hw_params(struct snd_pcm_substream *substream,
#define AC97_FMTS \
SNDRV_PCM_FMTBIT_S16_LE
-struct snd_soc_cpu_dai sh4_hac_dai[] = {
+struct snd_soc_dai sh4_hac_dai[] = {
{
.name = "HAC0",
.id = 0,
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
index 2f91de84c5c..92bfaf4774a 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -20,12 +20,12 @@
#define IPSEL 0xFE400034
/* platform specific structs can be declared here */
-extern struct snd_soc_cpu_dai sh4_hac_dai[2];
+extern struct snd_soc_dai sh4_hac_dai[2];
extern struct snd_soc_platform sh7760_soc_platform;
static int machine_init(struct snd_soc_codec *codec)
{
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index 3388bc3d62d..55c3464163a 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -208,7 +208,7 @@ static int ssi_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
+static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
unsigned int freq, int dir)
{
struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
@@ -222,7 +222,7 @@ static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
* This divider is used to generate the SSI_SCK (I2S bitclock) from the
* clock at the HAC_BIT_CLK ("oversampling clock") pin.
*/
-static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
+static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
{
struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
unsigned long ssicr;
@@ -245,7 +245,7 @@ static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
return 0;
}
-static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
+static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
unsigned long ssicr = SSIREG(SSICR);
@@ -332,7 +332,7 @@ static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
-struct snd_soc_cpu_dai sh4_ssi_dai[] = {
+struct snd_soc_dai sh4_ssi_dai[] = {
{
.name = "SSI0",
.id = 0,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e148db940cf..462e635dfc7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -4,8 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
* with code, comments and ideas from :-
* Richard Purdie <richard@openedhand.com>
*
@@ -14,10 +13,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 12th Aug 2005 Initial version.
- * 25th Oct 2005 Working Codec, Interface and Platform registration.
- *
* TODO:
* o Add hw rules to enforce rates, etc.
* o More testing with other codecs/machines.
@@ -112,9 +107,9 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
}
#endif
-static inline const char* get_dai_name(int type)
+static inline const char *get_dai_name(int type)
{
- switch(type) {
+ switch (type) {
case SND_SOC_DAI_AC97_BUS:
case SND_SOC_DAI_AC97:
return "AC97";
@@ -138,8 +133,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
@@ -182,9 +177,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
/* Check that the codec and cpu DAI's are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
- max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min);
+ max(codec_dai->playback.rate_min,
+ cpu_dai->playback.rate_min);
runtime->hw.rate_max =
- min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max);
+ min(codec_dai->playback.rate_max,
+ cpu_dai->playback.rate_max);
runtime->hw.channels_min =
max(codec_dai->playback.channels_min,
cpu_dai->playback.channels_min);
@@ -197,9 +194,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
codec_dai->playback.rates & cpu_dai->playback.rates;
} else {
runtime->hw.rate_min =
- max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min);
+ max(codec_dai->capture.rate_min,
+ cpu_dai->capture.rate_min);
runtime->hw.rate_max =
- min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max);
+ min(codec_dai->capture.rate_max,
+ cpu_dai->capture.rate_max);
runtime->hw.channels_min =
max(codec_dai->capture.channels_min,
cpu_dai->capture.channels_min);
@@ -229,7 +228,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
goto machine_err;
}
- dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
+ dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
@@ -272,11 +271,11 @@ static void close_delayed_work(struct work_struct *work)
struct snd_soc_device *socdev =
container_of(work, struct snd_soc_device, delayed_work.work);
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_codec_dai *codec_dai;
+ struct snd_soc_dai *codec_dai;
int i;
mutex_lock(&pcm_mutex);
- for(i = 0; i < codec->num_dai; i++) {
+ for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
dbg("pop wq checking: %s status: %s waiting: %s\n",
@@ -287,12 +286,12 @@ 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 */
+ /* Reduce power 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);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
}
codec_dai->pop_wait = 0;
@@ -300,12 +299,12 @@ static void close_delayed_work(struct work_struct *work)
codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_STOP);
- /* power down the codec power domain if no longer active */
+ /* Fall into standby if no longer active */
if (codec->active == 0) {
dbg("pop wq D3 %s %s\n", codec->name,
codec_dai->playback.stream_name);
- snd_soc_dapm_device_event(socdev,
- SNDRV_CTL_POWER_D3hot);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_STANDBY);
}
}
}
@@ -323,8 +322,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
mutex_lock(&pcm_mutex);
@@ -340,6 +339,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
}
codec->active--;
+ /* Muting the DAC suppresses artifacts caused during digital
+ * shutdown, for example from stopping clocks.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dai_digital_mute(codec_dai, 1);
+
if (cpu_dai->ops.shutdown)
cpu_dai->ops.shutdown(substream);
@@ -365,8 +370,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
SND_SOC_DAPM_STREAM_STOP);
if (codec->active == 0 && codec_dai->pop_wait == 0)
- snd_soc_dapm_device_event(socdev,
- SNDRV_CTL_POWER_D3hot);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_STANDBY);
}
mutex_unlock(&pcm_mutex);
@@ -384,8 +389,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
int ret = 0;
@@ -434,14 +439,14 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
else {
codec_dai->pop_wait = 0;
cancel_delayed_work(&socdev->delayed_work);
- if (codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 0);
+ snd_soc_dai_digital_mute(codec_dai, 0);
}
} else {
/* no delayed work - do we need to power up codec */
- if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+ if (codec->bias_level != SND_SOC_BIAS_ON) {
- snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D1);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dapm_stream_event(codec,
@@ -452,9 +457,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- 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);
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+ snd_soc_dai_digital_mute(codec_dai, 0);
} else {
/* codec already powered - power on widgets */
@@ -466,8 +470,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- if (codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 0);
+
+ snd_soc_dai_digital_mute(codec_dai, 0);
}
}
@@ -488,8 +492,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
@@ -514,7 +518,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
if (cpu_dai->ops.hw_params) {
ret = cpu_dai->ops.hw_params(substream, params);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't set interface %s hw params\n",
+ printk(KERN_ERR "asoc: interface %s hw params failed\n",
cpu_dai->name);
goto interface_err;
}
@@ -523,7 +527,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
if (platform->pcm_ops->hw_params) {
ret = platform->pcm_ops->hw_params(substream, params);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't set platform %s hw params\n",
+ printk(KERN_ERR "asoc: platform %s hw params failed\n",
platform->name);
goto platform_err;
}
@@ -542,7 +546,7 @@ interface_err:
codec_dai->ops.hw_free(substream);
codec_err:
- if(machine->ops && machine->ops->hw_free)
+ if (machine->ops && machine->ops->hw_free)
machine->ops->hw_free(substream);
mutex_unlock(&pcm_mutex);
@@ -558,15 +562,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
mutex_lock(&pcm_mutex);
/* apply codec digital mute */
- if (!codec->active && codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 1);
+ if (!codec->active)
+ snd_soc_dai_digital_mute(codec_dai, 1);
/* free any machine hw params */
if (machine->ops && machine->ops->hw_free)
@@ -593,8 +597,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret;
if (codec_dai->ops.trigger) {
@@ -631,16 +635,26 @@ static struct snd_pcm_ops soc_pcm_ops = {
/* powers down audio subsystem for suspend */
static int soc_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec;
int i;
+ /* Due to the resume being scheduled into a workqueue we could
+ * suspend before that's finished - wait for it to complete.
+ */
+ snd_power_lock(codec->card);
+ snd_power_wait(codec->card, SNDRV_CTL_POWER_D0);
+ snd_power_unlock(codec->card);
+
+ /* we're going to block userspace touching us until resume completes */
+ snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
+
/* mute any active DAC's */
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
if (dai->dai_ops.digital_mute && dai->playback.active)
dai->dai_ops.digital_mute(dai, 1);
}
@@ -652,8 +666,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
if (machine->suspend_pre)
machine->suspend_pre(pdev, state);
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
cpu_dai->suspend(pdev, cpu_dai);
if (platform->suspend)
@@ -662,9 +676,9 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
/* close any waiting streams and save state */
run_delayed_work(&socdev->delayed_work);
- codec->suspend_dapm_state = codec->dapm_state;
+ codec->suspend_bias_level = codec->bias_level;
- for(i = 0; i < codec->num_dai; i++) {
+ for (i = 0; i < codec->num_dai; i++) {
char *stream = codec->dai[i].playback.stream_name;
if (stream != NULL)
snd_soc_dapm_stream_event(codec, stream,
@@ -678,8 +692,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
if (codec_dev->suspend)
codec_dev->suspend(pdev, state);
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
cpu_dai->suspend(pdev, cpu_dai);
}
@@ -690,21 +704,32 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-/* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+/* deferred resume work, so resume can complete before we finished
+ * setting our codec back up, which can be very slow on I2C
+ */
+static void soc_resume_deferred(struct work_struct *work)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct snd_soc_device *socdev = container_of(work,
+ struct snd_soc_device,
+ deferred_resume_work);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec;
+ struct platform_device *pdev = to_platform_device(socdev->dev);
int i;
+ /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
+ * so userspace apps are blocked from touching us
+ */
+
+ dev_info(socdev->dev, "starting resume work\n");
+
if (machine->resume_pre)
machine->resume_pre(pdev);
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
cpu_dai->resume(pdev, cpu_dai);
}
@@ -712,8 +737,8 @@ static int soc_resume(struct platform_device *pdev)
if (codec_dev->resume)
codec_dev->resume(pdev);
- for(i = 0; i < codec->num_dai; i++) {
- char* stream = codec->dai[i].playback.stream_name;
+ for (i = 0; i < codec->num_dai; i++) {
+ char *stream = codec->dai[i].playback.stream_name;
if (stream != NULL)
snd_soc_dapm_stream_event(codec, stream,
SND_SOC_DAPM_STREAM_RESUME);
@@ -723,15 +748,15 @@ static int soc_resume(struct platform_device *pdev)
SND_SOC_DAPM_STREAM_RESUME);
}
- /* unmute any active DAC's */
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+ /* unmute any active DACs */
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
if (dai->dai_ops.digital_mute && dai->playback.active)
dai->dai_ops.digital_mute(dai, 0);
}
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
cpu_dai->resume(pdev, cpu_dai);
if (platform->resume)
@@ -741,6 +766,22 @@ static int soc_resume(struct platform_device *pdev)
if (machine->resume_post)
machine->resume_post(pdev);
+ dev_info(socdev->dev, "resume work completed\n");
+
+ /* userspace can access us now we are back as we were before */
+ snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ dev_info(socdev->dev, "scheduling resume work\n");
+
+ if (!schedule_work(&socdev->deferred_resume_work))
+ dev_err(socdev->dev, "work item may be lost\n");
+
return 0;
}
@@ -760,33 +801,38 @@ static int soc_probe(struct platform_device *pdev)
if (machine->probe) {
ret = machine->probe(pdev);
- if(ret < 0)
+ if (ret < 0)
return ret;
}
for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->probe) {
- ret = cpu_dai->probe(pdev);
- if(ret < 0)
+ ret = cpu_dai->probe(pdev, cpu_dai);
+ if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev);
- if(ret < 0)
+ if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {
ret = platform->probe(pdev);
- if(ret < 0)
+ if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+#ifdef CONFIG_PM
+ /* deferred resume work */
+ INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+#endif
+
return 0;
platform_err:
@@ -795,9 +841,9 @@ platform_err:
cpu_dai_err:
for (i--; i >= 0; i--) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->remove)
- cpu_dai->remove(pdev);
+ cpu_dai->remove(pdev, cpu_dai);
}
if (machine->remove)
@@ -824,9 +870,9 @@ static int soc_remove(struct platform_device *pdev)
codec_dev->remove(pdev);
for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->remove)
- cpu_dai->remove(pdev);
+ cpu_dai->remove(pdev, cpu_dai);
}
if (machine->remove)
@@ -852,8 +898,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
struct snd_soc_dai_link *dai_link, int num)
{
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = dai_link->codec_dai;
+ struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
struct snd_soc_pcm_runtime *rtd;
struct snd_pcm *pcm;
char new_name[64];
@@ -868,7 +914,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
codec_dai->codec = socdev->codec;
/* check client and interface hw capabilities */
- sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name,
+ sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
get_dai_name(cpu_dai->type), num);
if (codec_dai->playback.channels_min)
@@ -879,7 +925,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
capture, &pcm);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+ printk(KERN_ERR "asoc: can't create pcm for codec %s\n",
+ codec->name);
kfree(rtd);
return ret;
}
@@ -928,8 +975,29 @@ static ssize_t codec_reg_show(struct device *dev,
step = codec->reg_cache_step;
count += sprintf(buf, "%s registers\n", codec->name);
- for(i = 0; i < codec->reg_cache_size; i += step)
- count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i));
+ for (i = 0; i < codec->reg_cache_size; i += step) {
+ count += sprintf(buf + count, "%2x: ", i);
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ if (codec->display_register)
+ count += codec->display_register(codec, buf + count,
+ PAGE_SIZE - count, i);
+ else
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%4x", codec->read(codec, i));
+
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ if (count >= PAGE_SIZE - 1)
+ break;
+ }
+
+ /* Truncate count; min() would cause a warning */
+ if (count >= PAGE_SIZE)
+ count = PAGE_SIZE - 1;
return count;
}
@@ -1072,7 +1140,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
/* create the pcms */
- for(i = 0; i < machine->num_links; i++) {
+ for (i = 0; i < machine->num_links; i++) {
ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm %s\n",
@@ -1102,7 +1170,7 @@ 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;
- for(i = 0; i < machine->num_links; i++) {
+ for (i = 0; i < machine->num_links; i++) {
if (socdev->machine->dai_link[i].init) {
err = socdev->machine->dai_link[i].init(codec);
if (err < 0) {
@@ -1111,7 +1179,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
continue;
}
}
- if (socdev->machine->dai_link[i].codec_dai->type ==
+ if (socdev->machine->dai_link[i].codec_dai->type ==
SND_SOC_DAI_AC97_BUS)
ac97 = 1;
}
@@ -1122,7 +1190,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
ret = snd_card_register(codec->card);
if (ret < 0) {
- printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
+ printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
codec->name);
goto out;
}
@@ -1146,7 +1214,7 @@ 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");
+ printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
mutex_unlock(&codec->mutex);
@@ -1166,13 +1234,13 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
#ifdef CONFIG_SND_SOC_AC97_BUS
- struct snd_soc_codec_dai *codec_dai;
+ struct snd_soc_dai *codec_dai;
int i;
#endif
mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
- for(i = 0; i < codec->num_dai; i++) {
+ for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
soc_ac97_dev_unregister(codec);
@@ -1253,10 +1321,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
- uinfo->value.enumerated.items = e->mask;
+ uinfo->value.enumerated.items = e->max;
- if (uinfo->value.enumerated.item > e->mask - 1)
- uinfo->value.enumerated.item = e->mask - 1;
+ if (uinfo->value.enumerated.item > e->max - 1)
+ uinfo->value.enumerated.item = e->max - 1;
strcpy(uinfo->value.enumerated.name,
e->texts[uinfo->value.enumerated.item]);
return 0;
@@ -1279,10 +1347,11 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val, bitmask;
- for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
val = snd_soc_read(codec, e->reg);
- ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+ ucontrol->value.enumerated.item[0]
+ = (val >> e->shift_l) & (bitmask - 1);
if (e->shift_l != e->shift_r)
ucontrol->value.enumerated.item[1] =
(val >> e->shift_r) & (bitmask - 1);
@@ -1308,14 +1377,14 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned short val;
unsigned short mask, bitmask;
- for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
- if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+ if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
val = ucontrol->value.enumerated.item[0] << e->shift_l;
mask = (bitmask - 1) << e->shift_l;
if (e->shift_l != e->shift_r) {
- if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+ if (ucontrol->value.enumerated.item[1] > e->max - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
mask |= (bitmask - 1) << e->shift_r;
@@ -1342,10 +1411,10 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = e->mask;
+ uinfo->value.enumerated.items = e->max;
- if (uinfo->value.enumerated.item > e->mask - 1)
- uinfo->value.enumerated.item = e->mask - 1;
+ if (uinfo->value.enumerated.item > e->max - 1)
+ uinfo->value.enumerated.item = e->max - 1;
strcpy(uinfo->value.enumerated.name,
e->texts[uinfo->value.enumerated.item]);
return 0;
@@ -1390,9 +1459,11 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- int max = (kcontrol->private_value >> 16) & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max = mc->max;
+ unsigned int shift = mc->min;
+ unsigned int rshift = mc->rshift;
if (max == 1)
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -1418,13 +1489,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int rshift = (kcontrol->private_value >> 12) & 0x0f;
- int max = (kcontrol->private_value >> 16) & 0xff;
- int mask = (1 << fls(max)) - 1;
- int invert = (kcontrol->private_value >> 24) & 0x01;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
ucontrol->value.integer.value[0] =
(snd_soc_read(codec, reg) >> shift) & mask;
@@ -1455,13 +1528,15 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int rshift = (kcontrol->private_value >> 12) & 0x0f;
- int max = (kcontrol->private_value >> 16) & 0xff;
- int mask = (1 << fls(max)) - 1;
- int invert = (kcontrol->private_value >> 24) & 0x01;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
unsigned short val, val2, val_mask;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1493,7 +1568,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- int max = (kcontrol->private_value >> 12) & 0xff;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max = mc->max;
if (max == 1)
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -1519,13 +1596,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int reg2 = (kcontrol->private_value >> 24) & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int max = (kcontrol->private_value >> 12) & 0xff;
- int mask = (1<<fls(max))-1;
- int invert = (kcontrol->private_value >> 20) & 0x01;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1<<fls(max))-1;
+ unsigned int invert = mc->invert;
ucontrol->value.integer.value[0] =
(snd_soc_read(codec, reg) >> shift) & mask;
@@ -1554,13 +1633,15 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r);
int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int reg2 = (kcontrol->private_value >> 24) & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int max = (kcontrol->private_value >> 12) & 0xff;
- int mask = (1 << fls(max)) - 1;
- int invert = (kcontrol->private_value >> 20) & 0x01;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
int err;
unsigned short val, val2, val_mask;
@@ -1576,7 +1657,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0)
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
return err;
err = snd_soc_update_bits(codec, reg2, val_mask, val2);
@@ -1584,6 +1666,210 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
+/**
+ * snd_soc_info_volsw_s8 - signed mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max = mc->max;
+ int min = mc->min;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = max-min;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
+
+/**
+ * snd_soc_get_volsw_s8 - signed mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ int min = mc->min;
+ int val = snd_soc_read(codec, reg);
+
+ ucontrol->value.integer.value[0] =
+ ((signed char)(val & 0xff))-min;
+ ucontrol->value.integer.value[1] =
+ ((signed char)((val >> 8) & 0xff))-min;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
+
+/**
+ * snd_soc_put_volsw_sgn - signed mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ int min = mc->min;
+ unsigned short val;
+
+ val = (ucontrol->value.integer.value[0]+min) & 0xff;
+ val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
+
+ return snd_soc_update_bits(codec, reg, 0xffff, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
+
+/**
+ * snd_soc_dai_set_sysclk - configure DAI system or master clock.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
+ *
+ * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
+ */
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ if (dai->dai_ops.set_sysclk)
+ return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
+
+/**
+ * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
+ * @dai: DAI
+ * @clk_id: DAI specific clock divider ID
+ * @div: new clock divisor.
+ *
+ * Configures the clock dividers. This is used to derive the best DAI bit and
+ * frame clocks from the system or master clock. It's best to set the DAI bit
+ * and frame clocks as low as possible to save system power.
+ */
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+ int div_id, int div)
+{
+ if (dai->dai_ops.set_clkdiv)
+ return dai->dai_ops.set_clkdiv(dai, div_id, div);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
+
+/**
+ * snd_soc_dai_set_pll - configure DAI PLL.
+ * @dai: DAI
+ * @pll_id: DAI specific PLL ID
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
+ *
+ * Configures and enables PLL to generate output clock based on input clock.
+ */
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ if (dai->dai_ops.set_pll)
+ return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
+
+/**
+ * snd_soc_dai_set_fmt - configure DAI hardware audio format.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @fmt: SND_SOC_DAIFMT_ format value.
+ *
+ * Configures the DAI hardware format and clocking.
+ */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ if (dai->dai_ops.set_fmt)
+ return dai->dai_ops.set_fmt(dai, fmt);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
+
+/**
+ * snd_soc_dai_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @mask: DAI specific mask representing used slots.
+ * @slots: Number of slots in use.
+ *
+ * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
+ * specific.
+ */
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int mask, int slots)
+{
+ if (dai->dai_ops.set_sysclk)
+ return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
+
+/**
+ * snd_soc_dai_set_tristate - configure DAI system or master clock.
+ * @dai: DAI
+ * @tristate: tristate enable
+ *
+ * Tristates the DAI so that others can use it.
+ */
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ if (dai->dai_ops.set_sysclk)
+ return dai->dai_ops.set_tristate(dai, tristate);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
+
+/**
+ * snd_soc_dai_digital_mute - configure DAI system or master clock.
+ * @dai: DAI
+ * @mute: mute enable
+ *
+ * Mutes the DAI DAC.
+ */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ if (dai->dai_ops.digital_mute)
+ return dai->dai_ops.digital_mute(dai, mute);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
+
static int __devinit snd_soc_init(void)
{
printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
@@ -1592,14 +1878,14 @@ static int __devinit snd_soc_init(void)
static void snd_soc_exit(void)
{
- platform_driver_unregister(&soc_driver);
+ platform_driver_unregister(&soc_driver);
}
module_init(snd_soc_init);
module_exit(snd_soc_exit);
/* Module information */
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("ALSA SoC Core");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:soc-audio");
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index af3326c6350..efbd0b37810 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2,19 +2,13 @@
* soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
*
* Copyright 2005 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 12th Aug 2005 Initial version.
- * 25th Oct 2005 Implemented path power domain.
- * 18th Dec 2005 Implemented machine and stream level power domain.
- *
* Features:
* o Changes power status of internal codec blocks depending on the
* dynamic configuration of codec internal audio paths and active
@@ -43,6 +37,7 @@
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -50,23 +45,10 @@
#include <sound/initval.h>
/* debug */
-#define DAPM_DEBUG 0
-#if DAPM_DEBUG
+#ifdef DEBUG
#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
-#define dbg(format, arg...) printk(format, ## arg)
#else
#define dump_dapm(codec, action)
-#define dbg(format, arg...)
-#endif
-
-#define POP_DEBUG 0
-#if POP_DEBUG
-#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
-#define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
-#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
-#else
-#define pop_dbg(format, arg...)
-#define pop_wait(time)
#endif
/* dapm power sequences - make this per codec in the future */
@@ -85,6 +67,30 @@ static int dapm_status = 1;
module_param(dapm_status, int, 0);
MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
+static struct dentry *asoc_debugfs;
+
+static u32 pop_time;
+
+static void pop_wait(void)
+{
+ if (pop_time)
+ schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
+}
+
+static void pop_dbg(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (pop_time) {
+ vprintk(fmt, args);
+ pop_wait();
+ }
+
+ va_end(args);
+}
+
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
@@ -100,10 +106,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer: {
int val;
- int reg = w->kcontrols[i].private_value & 0xff;
- int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
- int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
- int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)
+ w->kcontrols[i].private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
val = snd_soc_read(w->codec, reg);
val = (val >> shift) & mask;
@@ -118,13 +127,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
int val, item, bitmask;
- for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
val = snd_soc_read(w->codec, e->reg);
item = (val >> e->shift_l) & (bitmask - 1);
p->connect = 0;
- for (i = 0; i < e->mask; i++) {
+ for (i = 0; i < e->max; i++) {
if (!(strcmp(p->name, e->texts[i])) && item == i)
p->connect = 1;
}
@@ -161,7 +170,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
int i;
- for (i = 0; i < e->mask; i++) {
+ for (i = 0; i < e->max; i++) {
if (!(strcmp(control_name, e->texts[i]))) {
list_add(&path->list, &codec->dapm_paths);
list_add(&path->list_sink, &dest->sources);
@@ -222,11 +231,12 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
change = old != new;
if (change) {
pop_dbg("pop test %s : %s in %d ms\n", widget->name,
- widget->power ? "on" : "off", POP_TIME);
+ widget->power ? "on" : "off", pop_time);
snd_soc_write(codec, widget->reg, new);
- pop_wait(POP_TIME);
+ pop_wait();
}
- dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
+ pr_debug("reg %x old %x new %x change %d\n", widget->reg,
+ old, new, change);
return change;
}
@@ -242,16 +252,19 @@ static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
return 0;
if (widget->num_kcontrols && k) {
- int reg = k->private_value & 0xff;
- int shift = (k->private_value >> 8) & 0x0f;
- int mask = (k->private_value >> 16) & 0xff;
- int invert = (k->private_value >> 24) & 0x01;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)k->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
if (power) {
int i;
/* power up has happended, increase volume to last level */
if (invert) {
- for (i = mask; i > widget->saved_value; i--)
+ for (i = max; i > widget->saved_value; i--)
snd_soc_update_bits(widget->codec, reg, mask, i);
} else {
for (i = 0; i < widget->saved_value; i++)
@@ -448,6 +461,26 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
}
/*
+ * Handler for generic register modifier widget.
+ */
+int dapm_reg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ val = w->on_val;
+ else
+ val = w->off_val;
+
+ snd_soc_update_bits(w->codec, -(w->reg + 1),
+ w->mask << w->shift, val << w->shift);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dapm_reg_event);
+
+/*
* Scan each dapm widget for complete audio path.
* A complete path is a route that has valid endpoints i.e.:-
*
@@ -499,24 +532,6 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
continue;
}
- /* programmable gain/attenuation */
- if (w->id == snd_soc_dapm_pga) {
- int on;
- in = is_connected_input_ep(w);
- dapm_clear_walk(w->codec);
- out = is_connected_output_ep(w);
- dapm_clear_walk(w->codec);
- w->power = on = (out != 0 && in != 0) ? 1 : 0;
-
- if (!on)
- dapm_set_pga(w, on); /* lower volume to reduce pops */
- dapm_update_bits(w);
- if (on)
- dapm_set_pga(w, on); /* restore volume from zero */
-
- continue;
- }
-
/* pre and post event widgets */
if (w->id == snd_soc_dapm_pre) {
if (!w->event)
@@ -562,45 +577,56 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
power_change = (w->power == power) ? 0: 1;
w->power = power;
+ if (!power_change)
+ continue;
+
/* call any power change event handlers */
- if (power_change) {
- if (w->event) {
- dbg("power %s event for %s flags %x\n",
- w->power ? "on" : "off", w->name, w->event_flags);
- if (power) {
- /* power up event */
- if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
- ret = w->event(w,
- 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,
- 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,
- 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,
- NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
- }
- } else
- /* no event handler */
- dapm_update_bits(w);
+ if (w->event)
+ pr_debug("power %s event for %s flags %x\n",
+ w->power ? "on" : "off",
+ w->name, w->event_flags);
+
+ /* power up pre event */
+ if (power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* power down pre event */
+ if (!power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Lower PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && !power)
+ dapm_set_pga(w, power);
+
+ dapm_update_bits(w);
+
+ /* Raise PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && power)
+ dapm_set_pga(w, power);
+
+ /* power up post event */
+ if (power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* power down post event */
+ if (!power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+ ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
}
}
}
@@ -608,7 +634,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
return ret;
}
-#if DAPM_DEBUG
+#ifdef DEBUG
static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
{
struct snd_soc_dapm_widget *w;
@@ -666,7 +692,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
/* test and update the power status of a mux widget */
static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int mask,
- int val, struct soc_enum* e)
+ int mux, int val, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -682,19 +708,21 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
if (path->kcontrol != kcontrol)
continue;
- if (!path->name || ! e->texts[val])
+ if (!path->name || !e->texts[mux])
continue;
found = 1;
/* we now need to match the string in the enum to the path */
- if (!(strcmp(path->name, e->texts[val])))
+ if (!(strcmp(path->name, e->texts[mux])))
path->connect = 1; /* new connection */
else
path->connect = 0; /* old connection must be powered down */
}
- if (found)
+ if (found) {
dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+ dump_dapm(widget->codec, "mux power update");
+ }
return 0;
}
@@ -730,8 +758,10 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
break;
}
- if (found)
+ if (found) {
dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+ dump_dapm(widget->codec, "mixer power update");
+ }
return 0;
}
@@ -768,21 +798,18 @@ static ssize_t dapm_widget_show(struct device *dev,
}
}
- switch(codec->dapm_state){
- case SNDRV_CTL_POWER_D0:
- state = "D0";
+ switch (codec->bias_level) {
+ case SND_SOC_BIAS_ON:
+ state = "On";
break;
- case SNDRV_CTL_POWER_D1:
- state = "D1";
+ case SND_SOC_BIAS_PREPARE:
+ state = "Prepare";
break;
- case SNDRV_CTL_POWER_D2:
- state = "D2";
+ case SND_SOC_BIAS_STANDBY:
+ state = "Standby";
break;
- case SNDRV_CTL_POWER_D3hot:
- state = "D3hot";
- break;
- case SNDRV_CTL_POWER_D3cold:
- state = "D3cold";
+ case SND_SOC_BIAS_OFF:
+ state = "Off";
break;
}
count += sprintf(buf + count, "PM State: %s\n", state);
@@ -796,16 +823,31 @@ int snd_soc_dapm_sys_add(struct device *dev)
{
int ret = 0;
- if (dapm_status)
- ret = device_create_file(dev, &dev_attr_dapm_widget);
+ if (!dapm_status)
+ return 0;
+
+ ret = device_create_file(dev, &dev_attr_dapm_widget);
+ if (ret != 0)
+ return ret;
- return ret;
+ asoc_debugfs = debugfs_create_dir("asoc", NULL);
+ if (!IS_ERR(asoc_debugfs))
+ debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs,
+ &pop_time);
+ else
+ asoc_debugfs = NULL;
+
+ return 0;
}
static void snd_soc_dapm_sys_remove(struct device *dev)
{
- if (dapm_status)
+ if (dapm_status) {
device_remove_file(dev, &dev_attr_dapm_widget);
+ }
+
+ if (asoc_debugfs)
+ debugfs_remove_recursive(asoc_debugfs);
}
/* free all dapm widgets and resources */
@@ -826,8 +868,25 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
}
}
+static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
+ char *pin, int status)
+{
+ struct snd_soc_dapm_widget *w;
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (!strcmp(w->name, pin)) {
+ pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+ w->connected = status;
+ return 0;
+ }
+ }
+
+ pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+ return -EINVAL;
+}
+
/**
- * snd_soc_dapm_sync_endpoints - scan and power dapm paths
+ * snd_soc_dapm_sync - scan and power dapm paths
* @codec: audio codec
*
* Walks all dapm audio paths and powers widgets according to their
@@ -835,27 +894,16 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
*
* Returns 0 for success.
*/
-int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
+int snd_soc_dapm_sync(struct snd_soc_codec *codec)
{
- return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+ int ret = dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+ dump_dapm(codec, "sync");
+ return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
-/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
- const char * control, const char *source)
+static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
+ const char *sink, const char *control, const char *source)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
@@ -957,9 +1005,64 @@ err:
kfree(path);
return ret;
}
+
+/**
+ * snd_soc_dapm_connect_input - connect dapm widgets
+ * @codec: audio codec
+ * @sink: name of target widget
+ * @control: mixer control name
+ * @source: name of source name
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * This function has been deprecated in favour of snd_soc_dapm_add_routes().
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
+ const char *control, const char *source)
+{
+ return snd_soc_dapm_add_route(codec, sink, control, source);
+}
EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
/**
+ * snd_soc_dapm_add_routes - Add routes between DAPM widgets
+ * @codec: codec
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * Returns 0 for success else error. On error all resources can be freed
+ * with a call to snd_soc_card_free().
+ */
+int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+ const struct snd_soc_dapm_route *route, int num)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ ret = snd_soc_dapm_add_route(codec, route->sink,
+ route->control, route->source);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to add route %s->%s\n",
+ route->source,
+ route->sink);
+ return ret;
+ }
+ route++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+
+/**
* snd_soc_dapm_new_widgets - add new dapm widgets
* @codec: audio codec
*
@@ -1022,12 +1125,14 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int rshift = (kcontrol->private_value >> 12) & 0x0f;
- int max = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0x01;
- int mask = (1 << fls(max)) - 1;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ unsigned int mask = (1 << fls(max)) - 1;
/* return the saved value if we are powered down */
if (widget->id == snd_soc_dapm_pga && !widget->power) {
@@ -1065,12 +1170,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0x0f;
- int rshift = (kcontrol->private_value >> 12) & 0x0f;
- int max = (kcontrol->private_value >> 16) & 0xff;
- int mask = (1 << fls(max)) - 1;
- int invert = (kcontrol->private_value >> 24) & 0x01;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
unsigned short val, val2, val_mask;
int ret;
@@ -1137,7 +1244,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val, bitmask;
- for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
val = snd_soc_read(widget->codec, e->reg);
ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
@@ -1167,15 +1274,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned short mask, bitmask;
int ret = 0;
- for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+ for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
- if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+ if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
mux = ucontrol->value.enumerated.item[0];
val = mux << e->shift_l;
mask = (bitmask - 1) << e->shift_l;
if (e->shift_l != e->shift_r) {
- if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+ if (ucontrol->value.enumerated.item[1] > e->max - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
mask |= (bitmask - 1) << e->shift_r;
@@ -1183,7 +1290,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock(&widget->codec->mutex);
widget->value = val;
- dapm_mux_update_power(widget, kcontrol, mask, mux, e);
+ dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
if (widget->event) {
if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
ret = widget->event(widget,
@@ -1234,6 +1341,33 @@ int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
/**
+ * snd_soc_dapm_new_controls - create new dapm controls
+ * @codec: audio codec
+ * @widget: widget array
+ * @num: number of widgets
+ *
+ * Creates new DAPM controls based upon the templates.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+ const struct snd_soc_dapm_widget *widget,
+ int num)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ ret = snd_soc_dapm_new_control(codec, widget);
+ if (ret < 0)
+ return ret;
+ widget++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+
+
+/**
* snd_soc_dapm_stream_event - send a stream event to the dapm core
* @codec: audio codec
* @stream: stream name
@@ -1257,8 +1391,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
{
if (!w->sname)
continue;
- dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
- stream, event);
+ pr_debug("widget %s\n %s stream %s event %d\n",
+ w->name, w->sname, stream, event);
if (strstr(w->sname, stream)) {
switch(event) {
case SND_SOC_DAPM_STREAM_START:
@@ -1294,53 +1428,101 @@ 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
+ * snd_soc_dapm_set_bias_level - set the bias level for the system
* @socdev: audio device
- * @event: device event
+ * @level: level to configure
*
- * Sends a device event to the dapm core. The core then makes any
- * necessary machine or codec power changes..
+ * Configure the bias (power) levels for the SoC audio device.
*
* Returns 0 for success else error.
*/
-int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
+int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+ enum snd_soc_bias_level level)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_machine *machine = socdev->machine;
+ int ret = 0;
- if (machine->dapm_event)
- machine->dapm_event(machine, event);
- if (codec->dapm_event)
- codec->dapm_event(codec, event);
- return 0;
+ if (machine->set_bias_level)
+ ret = machine->set_bias_level(machine, level);
+ if (ret == 0 && codec->set_bias_level)
+ ret = codec->set_bias_level(codec, level);
+
+ return ret;
+}
+
+/**
+ * snd_soc_dapm_enable_pin - enable pin.
+ * @snd_soc_codec: SoC codec
+ * @pin: pin name
+ *
+ * Enables input/output pin and it's parents or children widgets iff there is
+ * a valid audio route and active audio stream.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+{
+ return snd_soc_dapm_set_pin(codec, pin, 1);
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
/**
- * snd_soc_dapm_set_endpoint - set audio endpoint status
+ * snd_soc_dapm_disable_pin - disable pin.
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Disables input/output pin and it's parents or children widgets.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+{
+ return snd_soc_dapm_set_pin(codec, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
+
+/**
+ * snd_soc_dapm_nc_pin - permanently disable pin.
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Marks the specified pin as being not connected, disabling it along
+ * any parent or child widgets. At present this is identical to
+ * snd_soc_dapm_disable_pin() but in future it will be extended to do
+ * additional things such as disabling controls which only affect
+ * paths through the pin.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin)
+{
+ return snd_soc_dapm_set_pin(codec, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
+
+/**
+ * snd_soc_dapm_get_pin_status - get audio pin status
* @codec: audio codec
- * @endpoint: audio signal endpoint (or start point)
- * @status: point status
+ * @pin: audio signal pin endpoint (or start point)
*
- * Set audio endpoint status - connected or disconnected.
+ * Get audio pin status - connected or disconnected.
*
- * Returns 0 for success else error.
+ * Returns 1 for connected otherwise 0.
*/
-int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
- char *endpoint, int status)
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
{
struct snd_soc_dapm_widget *w;
list_for_each_entry(w, &codec->dapm_widgets, list) {
- if (!strcmp(w->name, endpoint)) {
- w->connected = status;
- return 0;
- }
+ if (!strcmp(w->name, pin))
+ return w->connected;
}
- return -ENODEV;
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
/**
* snd_soc_dapm_free - free dapm resources
@@ -1358,6 +1540,6 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev)
EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
/* Module information */
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
MODULE_LICENSE("GPL");
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 46daca17550..faef87a9bc3 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -1,5 +1,61 @@
/*
- * Sound core handling. Breaks out sound functions to submodules
+ * Sound core. This file is composed of two parts. sound_class
+ * which is common to both OSS and ALSA and OSS sound core which
+ * is used OSS or emulation of it.
+ */
+
+/*
+ * First, the common part.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_SOUND_OSS_CORE
+static int __init init_oss_soundcore(void);
+static void cleanup_oss_soundcore(void);
+#else
+static inline int init_oss_soundcore(void) { return 0; }
+static inline void cleanup_oss_soundcore(void) { }
+#endif
+
+struct class *sound_class;
+EXPORT_SYMBOL(sound_class);
+
+MODULE_DESCRIPTION("Core sound module");
+MODULE_AUTHOR("Alan Cox");
+MODULE_LICENSE("GPL");
+
+static int __init init_soundcore(void)
+{
+ int rc;
+
+ rc = init_oss_soundcore();
+ if (rc)
+ return rc;
+
+ sound_class = class_create(THIS_MODULE, "sound");
+ if (IS_ERR(sound_class)) {
+ cleanup_oss_soundcore();
+ return PTR_ERR(sound_class);
+ }
+
+ return 0;
+}
+
+static void __exit cleanup_soundcore(void)
+{
+ cleanup_oss_soundcore();
+ class_destroy(sound_class);
+}
+
+module_init(init_soundcore);
+module_exit(cleanup_soundcore);
+
+
+#ifdef CONFIG_SOUND_OSS_CORE
+/*
+ * OSS sound core handling. Breaks out sound functions to submodules
*
* Author: Alan Cox <alan.cox@linux.org>
*
@@ -34,20 +90,17 @@
* locking at some point in 2.3.x.
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/sound.h>
#include <linux/major.h>
#include <linux/kmod.h>
-#include <linux/device.h>
#define SOUND_STEP 16
-
struct sound_unit
{
int unit_minor;
@@ -63,9 +116,6 @@ extern int msnd_classic_init(void);
extern int msnd_pinnacle_init(void);
#endif
-struct class *sound_class;
-EXPORT_SYMBOL(sound_class);
-
/*
* Low level list operator. Scan the ordered list, find a hole and
* join into it. Called with the lock asserted
@@ -171,7 +221,7 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
- s->name+6);
+ NULL, s->name+6);
return r;
fail:
@@ -464,6 +514,8 @@ int soundcore_open(struct inode *inode, struct file *file)
struct sound_unit *s;
const struct file_operations *new_fops = NULL;
+ lock_kernel ();
+
chain=unit&0x0F;
if(chain==4 || chain==5) /* dsp/audio/dsp16 */
{
@@ -511,37 +563,31 @@ int soundcore_open(struct inode *inode, struct file *file)
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
+ unlock_kernel();
return err;
}
spin_unlock(&sound_loader_lock);
+ unlock_kernel();
return -ENODEV;
}
-MODULE_DESCRIPTION("Core sound module");
-MODULE_AUTHOR("Alan Cox");
-MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
-static void __exit cleanup_soundcore(void)
+static void cleanup_oss_soundcore(void)
{
/* We have nothing to really do here - we know the lists must be
empty */
unregister_chrdev(SOUND_MAJOR, "sound");
- class_destroy(sound_class);
}
-static int __init init_soundcore(void)
+static int __init init_oss_soundcore(void)
{
if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) {
printk(KERN_ERR "soundcore: sound device already in use.\n");
return -EBUSY;
}
- sound_class = class_create(THIS_MODULE, "sound");
- if (IS_ERR(sound_class))
- return PTR_ERR(sound_class);
return 0;
}
-module_init(init_soundcore);
-module_exit(cleanup_soundcore);
+#endif /* CONFIG_SOUND_OSS_CORE */
diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig
index 079e22af074..d75deba5617 100644
--- a/sound/sparc/Kconfig
+++ b/sound/sparc/Kconfig
@@ -1,11 +1,17 @@
# ALSA Sparc drivers
-menu "ALSA Sparc devices"
- depends on SND!=n && SPARC
+menuconfig SND_SPARC
+ bool "Sparc sound devices"
+ depends on SPARC
+ default y
+ help
+ Support for sound devices specific to Sun SPARC architectures.
+
+if SND_SPARC
config SND_SUN_AMD7930
tristate "Sun AMD7930"
- depends on SBUS && SND
+ depends on SBUS
select SND_PCM
help
Say Y here to include support for AMD7930 sound device on Sun.
@@ -15,7 +21,6 @@ config SND_SUN_AMD7930
config SND_SUN_CS4231
tristate "Sun CS4231"
- depends on SND
select SND_PCM
help
Say Y here to include support for CS4231 sound device on Sun.
@@ -25,7 +30,7 @@ config SND_SUN_CS4231
config SND_SUN_DBRI
tristate "Sun DBRI"
- depends on SND && SBUS
+ depends on SBUS
select SND_PCM
help
Say Y here to include support for DBRI sound device on Sun.
@@ -33,4 +38,4 @@ config SND_SUN_DBRI
To compile this driver as a module, choose M here: the module
will be called snd-sun-dbri.
-endmenu
+endif # SND_SPARC
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index 0c63e0585b1..f87933e4881 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -1,6 +1,6 @@
/*
* Driver for AMD7930 sound chips found on Sparcs.
- * Copyright (C) 2002 David S. Miller <davem@redhat.com>
+ * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
*
* Based entirely upon drivers/sbus/audio/amd7930.c which is:
* Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
@@ -35,6 +35,8 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -44,7 +46,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/sbus.h>
#include <asm/prom.h>
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
@@ -335,8 +336,8 @@ struct snd_amd7930 {
int pgain;
int mgain;
+ struct of_device *op;
unsigned int irq;
- unsigned int regs_size;
struct snd_amd7930 *next;
};
@@ -765,7 +766,6 @@ static int __devinit snd_amd7930_pcm(struct snd_amd7930 *amd)
/* playback count */ 1,
/* capture count */ 1, &pcm)) < 0)
return err;
- snd_assert(pcm != NULL, return -EINVAL);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops);
@@ -788,13 +788,6 @@ static int __devinit snd_amd7930_pcm(struct snd_amd7930 *amd)
static int snd_amd7930_info_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
{
- int type = kctl->private_value;
-
- snd_assert(type == VOLUME_MONITOR ||
- type == VOLUME_CAPTURE ||
- type == VOLUME_PLAYBACK, return -EINVAL);
- (void) type;
-
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
@@ -809,10 +802,6 @@ static int snd_amd7930_get_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem
int type = kctl->private_value;
int *swval;
- snd_assert(type == VOLUME_MONITOR ||
- type == VOLUME_CAPTURE ||
- type == VOLUME_PLAYBACK, return -EINVAL);
-
switch (type) {
case VOLUME_MONITOR:
swval = &amd->mgain;
@@ -838,10 +827,6 @@ static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem
int type = kctl->private_value;
int *swval, change;
- snd_assert(type == VOLUME_MONITOR ||
- type == VOLUME_CAPTURE ||
- type == VOLUME_PLAYBACK, return -EINVAL);
-
switch (type) {
case VOLUME_MONITOR:
swval = &amd->mgain;
@@ -904,7 +889,8 @@ static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd)
struct snd_card *card;
int idx, err;
- snd_assert(amd != NULL && amd->card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!amd || !amd->card))
+ return -EINVAL;
card = amd->card;
strcpy(card->mixername, card->shortname);
@@ -920,13 +906,16 @@ static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd)
static int snd_amd7930_free(struct snd_amd7930 *amd)
{
+ struct of_device *op = amd->op;
+
amd7930_idle(amd);
if (amd->irq)
free_irq(amd->irq, amd);
if (amd->regs)
- sbus_iounmap(amd->regs, amd->regs_size);
+ of_iounmap(&op->resource[0], amd->regs,
+ resource_size(&op->resource[0]));
kfree(amd);
@@ -945,13 +934,12 @@ static struct snd_device_ops snd_amd7930_dev_ops = {
};
static int __devinit snd_amd7930_create(struct snd_card *card,
- struct resource *rp,
- unsigned int reg_size,
+ struct of_device *op,
int irq, int dev,
struct snd_amd7930 **ramd)
{
- unsigned long flags;
struct snd_amd7930 *amd;
+ unsigned long flags;
int err;
*ramd = NULL;
@@ -961,9 +949,10 @@ static int __devinit snd_amd7930_create(struct snd_card *card,
spin_lock_init(&amd->lock);
amd->card = card;
- amd->regs_size = reg_size;
+ amd->op = op;
- amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930");
+ amd->regs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]), "amd7930");
if (!amd->regs) {
snd_printk("amd7930-%d: Unable to map chip registers.\n", dev);
return -EIO;
@@ -1012,12 +1001,15 @@ static int __devinit snd_amd7930_create(struct snd_card *card,
return 0;
}
-static int __devinit amd7930_attach_common(struct resource *rp, int irq)
+static int __devinit amd7930_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
+ struct resource *rp = &op->resource[0];
static int dev_num;
struct snd_card *card;
struct snd_amd7930 *amd;
- int err;
+ int err, irq;
+
+ irq = op->irqs[0];
if (dev_num >= SNDRV_CARDS)
return -ENODEV;
@@ -1038,8 +1030,7 @@ static int __devinit amd7930_attach_common(struct resource *rp, int irq)
(unsigned long long)rp->start,
irq);
- if ((err = snd_amd7930_create(card, rp,
- (rp->end - rp->start) + 1,
+ if ((err = snd_amd7930_create(card, op,
irq, dev_num, &amd)) < 0)
goto out_err;
@@ -1064,43 +1055,7 @@ out_err:
return err;
}
-static int __devinit amd7930_obio_attach(struct device_node *dp)
-{
- const struct linux_prom_registers *regs;
- const struct linux_prom_irqs *irqp;
- struct resource res, *rp;
- int len;
-
- irqp = of_get_property(dp, "intr", &len);
- if (!irqp) {
- snd_printk("%s: Firmware node lacks IRQ property.\n",
- dp->full_name);
- return -ENODEV;
- }
-
- regs = of_get_property(dp, "reg", &len);
- if (!regs) {
- snd_printk("%s: Firmware node lacks register property.\n",
- dp->full_name);
- return -ENODEV;
- }
-
- rp = &res;
- rp->start = regs->phys_addr;
- rp->end = rp->start + regs->reg_size - 1;
- rp->flags = IORESOURCE_IO | (regs->which_io & 0xff);
-
- return amd7930_attach_common(rp, irqp->pri);
-}
-
-static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]);
-}
-
-static struct of_device_id amd7930_match[] = {
+static const struct of_device_id amd7930_match[] = {
{
.name = "audio",
},
@@ -1115,20 +1070,7 @@ static struct of_platform_driver amd7930_sbus_driver = {
static int __init amd7930_init(void)
{
- struct device_node *dp;
-
- /* Try to find the sun4c "audio" node first. */
- dp = of_find_node_by_path("/");
- dp = dp->child;
- while (dp) {
- if (!strcmp(dp->name, "audio"))
- amd7930_obio_attach(dp);
-
- dp = dp->sibling;
- }
-
- /* Probe each SBUS for amd7930 chips. */
- return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&amd7930_sbus_driver, &of_bus_type);
}
static void __exit amd7930_exit(void)
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index 1c4797be72e..d44bf98e965 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -1,6 +1,6 @@
/*
* Driver for CS4231 sound chips found on Sparcs.
- * Copyright (C) 2002 David S. Miller <davem@redhat.com>
+ * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
*
* Based entirely upon drivers/sbus/audio/cs4231.c which is:
* Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
@@ -17,7 +17,8 @@
#include <linux/moduleparam.h>
#include <linux/irq.h>
#include <linux/io.h>
-
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -29,13 +30,12 @@
#ifdef CONFIG_SBUS
#define SBUS_SUPPORT
-#include <asm/sbus.h>
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)
#define EBUS_SUPPORT
#include <linux/pci.h>
-#include <asm/ebus.h>
+#include <asm/ebus_dma.h>
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
@@ -70,8 +70,6 @@ struct cs4231_dma_control {
int (*request)(struct cs4231_dma_control *dma_cont,
dma_addr_t bus_addr, size_t len);
unsigned int (*address)(struct cs4231_dma_control *dma_cont);
- void (*preallocate)(struct snd_cs4231 *chip,
- struct snd_pcm *pcm);
#ifdef EBUS_SUPPORT
struct ebus_dma_info ebus_info;
#endif
@@ -114,21 +112,12 @@ struct snd_cs4231 {
struct mutex mce_mutex; /* mutex for mce register */
struct mutex open_mutex; /* mutex for ALSA open/close */
- union {
-#ifdef SBUS_SUPPORT
- struct sbus_dev *sdev;
-#endif
-#ifdef EBUS_SUPPORT
- struct pci_dev *pdev;
-#endif
- } dev_u;
+ struct of_device *op;
unsigned int irq[2];
unsigned int regs_size;
struct snd_cs4231 *next;
};
-static struct snd_cs4231 *cs4231_list;
-
/* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for
* now.... -DaveM
*/
@@ -267,27 +256,19 @@ static unsigned char snd_cs4231_original_image[32] =
static u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr)
{
-#ifdef EBUS_SUPPORT
if (cp->flags & CS4231_FLAG_EBUS)
return readb(reg_addr);
else
-#endif
-#ifdef SBUS_SUPPORT
return sbus_readb(reg_addr);
-#endif
}
static void __cs4231_writeb(struct snd_cs4231 *cp, u8 val,
void __iomem *reg_addr)
{
-#ifdef EBUS_SUPPORT
if (cp->flags & CS4231_FLAG_EBUS)
return writeb(val, reg_addr);
else
-#endif
-#ifdef SBUS_SUPPORT
return sbus_writeb(val, reg_addr);
-#endif
}
/*
@@ -1258,7 +1239,9 @@ static int __init snd_cs4231_pcm(struct snd_card *card)
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
strcpy(pcm->name, "CS4231");
- chip->p_dma.preallocate(chip, pcm);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->op->dev,
+ 64 * 1024, 128 * 1024);
chip->pcm = pcm;
@@ -1560,7 +1543,8 @@ static int __init snd_cs4231_mixer(struct snd_card *card)
struct snd_cs4231 *chip = card->private_data;
int err, idx;
- snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
+ if (snd_BUG_ON(!chip || !chip->pcm))
+ return -EINVAL;
strcpy(card->mixername, chip->pcm->name);
@@ -1626,8 +1610,7 @@ static int __init cs4231_attach_finish(struct snd_card *card)
if (err < 0)
goto out_err;
- chip->next = cs4231_list;
- cs4231_list = chip;
+ dev_set_drvdata(&chip->op->dev, chip);
dev++;
return 0;
@@ -1782,24 +1765,19 @@ static unsigned int sbus_dma_addr(struct cs4231_dma_control *dma_cont)
return sbus_readl(base->regs + base->dir + APCVA);
}
-static void sbus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS,
- snd_dma_sbus_data(chip->dev_u.sdev),
- 64 * 1024, 128 * 1024);
-}
-
/*
* Init and exit routines
*/
static int snd_cs4231_sbus_free(struct snd_cs4231 *chip)
{
+ struct of_device *op = chip->op;
+
if (chip->irq[0])
free_irq(chip->irq[0], chip);
if (chip->port)
- sbus_iounmap(chip->port, chip->regs_size);
+ of_iounmap(&op->resource[0], chip->port, chip->regs_size);
return 0;
}
@@ -1816,7 +1794,7 @@ static struct snd_device_ops snd_cs4231_sbus_dev_ops = {
};
static int __init snd_cs4231_sbus_create(struct snd_card *card,
- struct sbus_dev *sdev,
+ struct of_device *op,
int dev)
{
struct snd_cs4231 *chip = card->private_data;
@@ -1827,13 +1805,13 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card,
spin_lock_init(&chip->p_dma.sbus_info.lock);
mutex_init(&chip->mce_mutex);
mutex_init(&chip->open_mutex);
- chip->dev_u.sdev = sdev;
- chip->regs_size = sdev->reg_addrs[0].reg_size;
+ chip->op = op;
+ chip->regs_size = resource_size(&op->resource[0]);
memcpy(&chip->image, &snd_cs4231_original_image,
sizeof(snd_cs4231_original_image));
- chip->port = sbus_ioremap(&sdev->resource[0], 0,
- chip->regs_size, "cs4231");
+ chip->port = of_ioremap(&op->resource[0], 0,
+ chip->regs_size, "cs4231");
if (!chip->port) {
snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev);
return -EIO;
@@ -1848,22 +1826,20 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card,
chip->p_dma.enable = sbus_dma_enable;
chip->p_dma.request = sbus_dma_request;
chip->p_dma.address = sbus_dma_addr;
- chip->p_dma.preallocate = sbus_dma_preallocate;
chip->c_dma.prepare = sbus_dma_prepare;
chip->c_dma.enable = sbus_dma_enable;
chip->c_dma.request = sbus_dma_request;
chip->c_dma.address = sbus_dma_addr;
- chip->c_dma.preallocate = sbus_dma_preallocate;
- if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt,
+ if (request_irq(op->irqs[0], snd_cs4231_sbus_interrupt,
IRQF_SHARED, "cs4231", chip)) {
snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n",
- dev, sdev->irqs[0]);
+ dev, op->irqs[0]);
snd_cs4231_sbus_free(chip);
return -EBUSY;
}
- chip->irq[0] = sdev->irqs[0];
+ chip->irq[0] = op->irqs[0];
if (snd_cs4231_probe(chip) < 0) {
snd_cs4231_sbus_free(chip);
@@ -1880,9 +1856,9 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card,
return 0;
}
-static int __init cs4231_sbus_attach(struct sbus_dev *sdev)
+static int __devinit cs4231_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- struct resource *rp = &sdev->resource[0];
+ struct resource *rp = &op->resource[0];
struct snd_card *card;
int err;
@@ -1894,9 +1870,9 @@ static int __init cs4231_sbus_attach(struct sbus_dev *sdev)
card->shortname,
rp->flags & 0xffL,
(unsigned long long)rp->start,
- sdev->irqs[0]);
+ op->irqs[0]);
- err = snd_cs4231_sbus_create(card, sdev, dev);
+ err = snd_cs4231_sbus_create(card, op, dev);
if (err < 0) {
snd_card_free(card);
return err;
@@ -1949,30 +1925,25 @@ static unsigned int _ebus_dma_addr(struct cs4231_dma_control *dma_cont)
return ebus_dma_addr(&dma_cont->ebus_info);
}
-static void _ebus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->dev_u.pdev),
- 64*1024, 128*1024);
-}
-
/*
* Init and exit routines
*/
static int snd_cs4231_ebus_free(struct snd_cs4231 *chip)
{
+ struct of_device *op = chip->op;
+
if (chip->c_dma.ebus_info.regs) {
ebus_dma_unregister(&chip->c_dma.ebus_info);
- iounmap(chip->c_dma.ebus_info.regs);
+ of_iounmap(&op->resource[2], chip->c_dma.ebus_info.regs, 0x10);
}
if (chip->p_dma.ebus_info.regs) {
ebus_dma_unregister(&chip->p_dma.ebus_info);
- iounmap(chip->p_dma.ebus_info.regs);
+ of_iounmap(&op->resource[1], chip->p_dma.ebus_info.regs, 0x10);
}
if (chip->port)
- iounmap(chip->port);
+ of_iounmap(&op->resource[0], chip->port, 0x10);
return 0;
}
@@ -1989,7 +1960,7 @@ static struct snd_device_ops snd_cs4231_ebus_dev_ops = {
};
static int __init snd_cs4231_ebus_create(struct snd_card *card,
- struct linux_ebus_device *edev,
+ struct of_device *op,
int dev)
{
struct snd_cs4231 *chip = card->private_data;
@@ -2001,35 +1972,35 @@ static int __init snd_cs4231_ebus_create(struct snd_card *card,
mutex_init(&chip->mce_mutex);
mutex_init(&chip->open_mutex);
chip->flags |= CS4231_FLAG_EBUS;
- chip->dev_u.pdev = edev->bus->self;
+ chip->op = op;
memcpy(&chip->image, &snd_cs4231_original_image,
sizeof(snd_cs4231_original_image));
strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)");
chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback;
chip->c_dma.ebus_info.client_cookie = chip;
- chip->c_dma.ebus_info.irq = edev->irqs[0];
+ chip->c_dma.ebus_info.irq = op->irqs[0];
strcpy(chip->p_dma.ebus_info.name, "cs4231(play)");
chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback;
chip->p_dma.ebus_info.client_cookie = chip;
- chip->p_dma.ebus_info.irq = edev->irqs[1];
+ chip->p_dma.ebus_info.irq = op->irqs[1];
chip->p_dma.prepare = _ebus_dma_prepare;
chip->p_dma.enable = _ebus_dma_enable;
chip->p_dma.request = _ebus_dma_request;
chip->p_dma.address = _ebus_dma_addr;
- chip->p_dma.preallocate = _ebus_dma_preallocate;
chip->c_dma.prepare = _ebus_dma_prepare;
chip->c_dma.enable = _ebus_dma_enable;
chip->c_dma.request = _ebus_dma_request;
chip->c_dma.address = _ebus_dma_addr;
- chip->c_dma.preallocate = _ebus_dma_preallocate;
- chip->port = ioremap(edev->resource[0].start, 0x10);
- chip->p_dma.ebus_info.regs = ioremap(edev->resource[1].start, 0x10);
- chip->c_dma.ebus_info.regs = ioremap(edev->resource[2].start, 0x10);
+ chip->port = of_ioremap(&op->resource[0], 0, 0x10, "cs4231");
+ chip->p_dma.ebus_info.regs =
+ of_ioremap(&op->resource[1], 0, 0x10, "cs4231_pdma");
+ chip->c_dma.ebus_info.regs =
+ of_ioremap(&op->resource[2], 0, 0x10, "cs4231_cdma");
if (!chip->port || !chip->p_dma.ebus_info.regs ||
!chip->c_dma.ebus_info.regs) {
snd_cs4231_ebus_free(chip);
@@ -2077,7 +2048,7 @@ static int __init snd_cs4231_ebus_create(struct snd_card *card,
return 0;
}
-static int __init cs4231_ebus_attach(struct linux_ebus_device *edev)
+static int __devinit cs4231_ebus_probe(struct of_device *op, const struct of_device_id *match)
{
struct snd_card *card;
int err;
@@ -2088,10 +2059,10 @@ static int __init cs4231_ebus_attach(struct linux_ebus_device *edev)
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname,
- edev->resource[0].start,
- edev->irqs[0]);
+ op->resource[0].start,
+ op->irqs[0]);
- err = snd_cs4231_ebus_create(card, edev, dev);
+ err = snd_cs4231_ebus_create(card, op, dev);
if (err < 0) {
snd_card_free(card);
return err;
@@ -2101,68 +2072,57 @@ static int __init cs4231_ebus_attach(struct linux_ebus_device *edev)
}
#endif
-static int __init cs4231_init(void)
+static int __devinit cs4231_probe(struct of_device *op, const struct of_device_id *match)
{
-#ifdef SBUS_SUPPORT
- struct sbus_bus *sbus;
- struct sbus_dev *sdev;
-#endif
#ifdef EBUS_SUPPORT
- struct linux_ebus *ebus;
- struct linux_ebus_device *edev;
+ if (!strcmp(op->node->parent->name, "ebus"))
+ return cs4231_ebus_probe(op, match);
#endif
- int found;
-
- found = 0;
-
#ifdef SBUS_SUPPORT
- for_all_sbusdev(sdev, sbus) {
- if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
- if (cs4231_sbus_attach(sdev) == 0)
- found++;
- }
- }
+ if (!strcmp(op->node->parent->name, "sbus") ||
+ !strcmp(op->node->parent->name, "sbi"))
+ return cs4231_sbus_probe(op, match);
#endif
-#ifdef EBUS_SUPPORT
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- int match = 0;
-
- if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) {
- match = 1;
- } else if (!strcmp(edev->prom_node->name, "audio")) {
- const char *compat;
-
- compat = of_get_property(edev->prom_node,
- "compatible", NULL);
- if (compat && !strcmp(compat, "SUNW,CS4231"))
- match = 1;
- }
+ return -ENODEV;
+}
- if (match &&
- cs4231_ebus_attach(edev) == 0)
- found++;
- }
- }
-#endif
+static int __devexit cs4231_remove(struct of_device *op)
+{
+ struct snd_cs4231 *chip = dev_get_drvdata(&op->dev);
+ snd_card_free(chip->card);
- return (found > 0) ? 0 : -EIO;
+ return 0;
}
-static void __exit cs4231_exit(void)
-{
- struct snd_cs4231 *p = cs4231_list;
+static const struct of_device_id cs4231_match[] = {
+ {
+ .name = "SUNW,CS4231",
+ },
+ {
+ .name = "audio",
+ .compatible = "SUNW,CS4231",
+ },
+ {},
+};
- while (p != NULL) {
- struct snd_cs4231 *next = p->next;
+MODULE_DEVICE_TABLE(of, cs4231_match);
- snd_card_free(p->card);
+static struct of_platform_driver cs4231_driver = {
+ .name = "audio",
+ .match_table = cs4231_match,
+ .probe = cs4231_probe,
+ .remove = __devexit_p(cs4231_remove),
+};
- p = next;
- }
+static int __init cs4231_init(void)
+{
+ return of_register_driver(&cs4231_driver, &of_bus_type);
+}
- cs4231_list = NULL;
+static void __exit cs4231_exit(void)
+{
+ of_unregister_driver(&cs4231_driver);
}
module_init(cs4231_init);
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 3d00e0797b1..c257ad8bdfb 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -57,6 +57,7 @@
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -66,7 +67,7 @@
#include <sound/initval.h>
#include <linux/of.h>
-#include <asm/sbus.h>
+#include <linux/of_device.h>
#include <asm/atomic.h>
MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
@@ -297,7 +298,7 @@ struct dbri_streaminfo {
/* This structure holds the information for both chips (DBRI & CS4215) */
struct snd_dbri {
int regs_size, irq; /* Needed for unload */
- struct sbus_dev *sdev; /* SBUS device info */
+ struct of_device *op; /* OF device info */
spinlock_t lock;
struct dbri_dma *dma; /* Pointer to our DMA block */
@@ -2093,14 +2094,15 @@ static int snd_dbri_hw_params(struct snd_pcm_substream *substream,
*/
if (info->dvma_buffer == 0) {
if (DBRI_STREAMNO(substream) == DBRI_PLAY)
- direction = SBUS_DMA_TODEVICE;
+ direction = DMA_TO_DEVICE;
else
- direction = SBUS_DMA_FROMDEVICE;
+ direction = DMA_FROM_DEVICE;
- info->dvma_buffer = sbus_map_single(dbri->sdev,
- runtime->dma_area,
- params_buffer_bytes(hw_params),
- direction);
+ info->dvma_buffer =
+ dma_map_single(&dbri->op->dev,
+ runtime->dma_area,
+ params_buffer_bytes(hw_params),
+ direction);
}
direction = params_buffer_bytes(hw_params);
@@ -2121,12 +2123,12 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream)
*/
if (info->dvma_buffer) {
if (DBRI_STREAMNO(substream) == DBRI_PLAY)
- direction = SBUS_DMA_TODEVICE;
+ direction = DMA_TO_DEVICE;
else
- direction = SBUS_DMA_FROMDEVICE;
+ direction = DMA_FROM_DEVICE;
- sbus_unmap_single(dbri->sdev, info->dvma_buffer,
- substream->runtime->buffer_size, direction);
+ dma_unmap_single(&dbri->op->dev, info->dvma_buffer,
+ substream->runtime->buffer_size, direction);
info->dvma_buffer = 0;
}
if (info->pipe != -1) {
@@ -2223,7 +2225,6 @@ static int __devinit snd_dbri_pcm(struct snd_card *card)
/* playback count */ 1,
/* capture count */ 1, &pcm)) < 0)
return err;
- snd_assert(pcm != NULL, return -EINVAL);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops);
@@ -2263,9 +2264,10 @@ static int snd_cs4215_get_volume(struct snd_kcontrol *kcontrol,
{
struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
struct dbri_streaminfo *info;
- snd_assert(dbri != NULL, return -EINVAL);
+
+ if (snd_BUG_ON(!dbri))
+ return -EINVAL;
info = &dbri->stream_info[kcontrol->private_value];
- snd_assert(info != NULL, return -EINVAL);
ucontrol->value.integer.value[0] = info->left_gain;
ucontrol->value.integer.value[1] = info->right_gain;
@@ -2331,7 +2333,9 @@ static int snd_cs4215_get_single(struct snd_kcontrol *kcontrol,
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 1;
- snd_assert(dbri != NULL, return -EINVAL);
+
+ if (snd_BUG_ON(!dbri))
+ return -EINVAL;
if (elem < 4)
ucontrol->value.integer.value[0] =
@@ -2356,7 +2360,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
int invert = (kcontrol->private_value >> 24) & 1;
int changed = 0;
unsigned short val;
- snd_assert(dbri != NULL, return -EINVAL);
+
+ if (snd_BUG_ON(!dbri))
+ return -EINVAL;
val = (ucontrol->value.integer.value[0] & mask);
if (invert == 1)
@@ -2432,7 +2438,8 @@ static int __devinit snd_dbri_mixer(struct snd_card *card)
int idx, err;
struct snd_dbri *dbri;
- snd_assert(card != NULL && card->private_data != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card || !card->private_data))
+ return -EINVAL;
dbri = card->private_data;
strcpy(card->mixername, card->shortname);
@@ -2490,7 +2497,7 @@ static void dbri_debug_read(struct snd_info_entry *entry,
}
#endif
-void __devinit snd_dbri_proc(struct snd_card *card)
+static void __devinit snd_dbri_proc(struct snd_card *card)
{
struct snd_dbri *dbri = card->private_data;
struct snd_info_entry *entry;
@@ -2514,31 +2521,32 @@ void __devinit snd_dbri_proc(struct snd_card *card)
static void snd_dbri_free(struct snd_dbri *dbri);
static int __devinit snd_dbri_create(struct snd_card *card,
- struct sbus_dev *sdev,
- int irq, int dev)
+ struct of_device *op,
+ int irq, int dev)
{
struct snd_dbri *dbri = card->private_data;
int err;
spin_lock_init(&dbri->lock);
- dbri->sdev = sdev;
+ dbri->op = op;
dbri->irq = irq;
- dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma),
- &dbri->dma_dvma);
+ dbri->dma = dma_alloc_coherent(&op->dev,
+ sizeof(struct dbri_dma),
+ &dbri->dma_dvma, GFP_ATOMIC);
memset((void *)dbri->dma, 0, sizeof(struct dbri_dma));
dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n",
dbri->dma, dbri->dma_dvma);
/* Map the registers into memory. */
- dbri->regs_size = sdev->reg_addrs[0].reg_size;
- dbri->regs = sbus_ioremap(&sdev->resource[0], 0,
- dbri->regs_size, "DBRI Registers");
+ dbri->regs_size = resource_size(&op->resource[0]);
+ dbri->regs = of_ioremap(&op->resource[0], 0,
+ dbri->regs_size, "DBRI Registers");
if (!dbri->regs) {
printk(KERN_ERR "DBRI: could not allocate registers\n");
- sbus_free_consistent(sdev, sizeof(struct dbri_dma),
- (void *)dbri->dma, dbri->dma_dvma);
+ dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
+ (void *)dbri->dma, dbri->dma_dvma);
return -EIO;
}
@@ -2546,9 +2554,9 @@ static int __devinit snd_dbri_create(struct snd_card *card,
"DBRI audio", dbri);
if (err) {
printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
- sbus_iounmap(dbri->regs, dbri->regs_size);
- sbus_free_consistent(sdev, sizeof(struct dbri_dma),
- (void *)dbri->dma, dbri->dma_dvma);
+ of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size);
+ dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
+ (void *)dbri->dma, dbri->dma_dvma);
return err;
}
@@ -2572,27 +2580,23 @@ static void snd_dbri_free(struct snd_dbri *dbri)
free_irq(dbri->irq, dbri);
if (dbri->regs)
- sbus_iounmap(dbri->regs, dbri->regs_size);
+ of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size);
if (dbri->dma)
- sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma),
- (void *)dbri->dma, dbri->dma_dvma);
+ dma_free_coherent(&dbri->op->dev,
+ sizeof(struct dbri_dma),
+ (void *)dbri->dma, dbri->dma_dvma);
}
-static int __devinit dbri_probe(struct of_device *of_dev,
- const struct of_device_id *match)
+static int __devinit dbri_probe(struct of_device *op, const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&of_dev->dev);
struct snd_dbri *dbri;
- int irq;
struct resource *rp;
struct snd_card *card;
static int dev = 0;
+ int irq;
int err;
- dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n",
- sdev->prom_name, sdev->slot);
-
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
@@ -2600,7 +2604,7 @@ static int __devinit dbri_probe(struct of_device *of_dev,
return -ENOENT;
}
- irq = sdev->irqs[0];
+ irq = op->irqs[0];
if (irq <= 0) {
printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev);
return -ENODEV;
@@ -2613,12 +2617,12 @@ static int __devinit dbri_probe(struct of_device *of_dev,
strcpy(card->driver, "DBRI");
strcpy(card->shortname, "Sun DBRI");
- rp = &sdev->resource[0];
+ rp = &op->resource[0];
sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
card->shortname,
rp->flags & 0xffL, (unsigned long long)rp->start, irq);
- err = snd_dbri_create(card, sdev, irq, dev);
+ err = snd_dbri_create(card, op, irq, dev);
if (err < 0) {
snd_card_free(card);
return err;
@@ -2635,7 +2639,7 @@ static int __devinit dbri_probe(struct of_device *of_dev,
/* /proc file handling */
snd_dbri_proc(card);
- dev_set_drvdata(&of_dev->dev, card);
+ dev_set_drvdata(&op->dev, card);
err = snd_card_register(card);
if (err < 0)
@@ -2643,7 +2647,7 @@ static int __devinit dbri_probe(struct of_device *of_dev,
printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
dev, dbri->regs,
- dbri->irq, sdev->prom_name[9], dbri->mm.version);
+ dbri->irq, op->node->name[9], dbri->mm.version);
dev++;
return 0;
@@ -2654,19 +2658,19 @@ _err:
return err;
}
-static int __devexit dbri_remove(struct of_device *dev)
+static int __devexit dbri_remove(struct of_device *op)
{
- struct snd_card *card = dev_get_drvdata(&dev->dev);
+ struct snd_card *card = dev_get_drvdata(&op->dev);
snd_dbri_free(card->private_data);
snd_card_free(card);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id dbri_match[] = {
+static const struct of_device_id dbri_match[] = {
{
.name = "SUNW,DBRIe",
},
@@ -2688,7 +2692,7 @@ static struct of_platform_driver dbri_sbus_driver = {
/* Probe for the dbri chip and then attach the driver. */
static int __init dbri_init(void)
{
- return of_register_driver(&dbri_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&dbri_sbus_driver, &of_bus_type);
}
static void __exit dbri_exit(void)
diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig
index 0d08c29213c..e6485be2e6f 100644
--- a/sound/spi/Kconfig
+++ b/sound/spi/Kconfig
@@ -1,7 +1,13 @@
#SPI drivers
-menu "SPI devices"
- depends on SND != n
+menuconfig SND_SPI
+ bool "SPI sound devices"
+ depends on SPI
+ default y
+ help
+ Support for sound devices connected via the SPI bus.
+
+if SND_SPI
config SND_AT73C213
tristate "Atmel AT73C213 DAC driver"
@@ -28,4 +34,5 @@ config SND_AT73C213_TARGET_BITRATE
Set to 48000 Hz by default.
-endmenu
+endif # SND_SPI
+
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index c89d2ea594b..f16a3fce459 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -93,10 +93,10 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
int err;
struct snd_sf_callback sf_cb;
- snd_assert(emu->hw != NULL, return -EINVAL);
- snd_assert(emu->max_voices > 0, return -EINVAL);
- snd_assert(card != NULL, return -EINVAL);
- snd_assert(name != NULL, return -EINVAL);
+ if (snd_BUG_ON(!emu->hw || emu->max_voices <= 0))
+ return -EINVAL;
+ if (snd_BUG_ON(!card || !name))
+ return -EINVAL;
emu->card = card;
emu->name = kstrdup(name, GFP_KERNEL);
diff --git a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c
index c6917ba2c93..00fc005ecf6 100644
--- a/sound/synth/emux/emux_nrpn.c
+++ b/sound/synth/emux/emux_nrpn.c
@@ -289,8 +289,8 @@ snd_emux_nrpn(void *p, struct snd_midi_channel *chan,
struct snd_emux_port *port;
port = p;
- snd_assert(port != NULL, return);
- snd_assert(chan != NULL, return);
+ if (snd_BUG_ON(!port || !chan))
+ return;
if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 &&
chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) {
@@ -379,8 +379,8 @@ snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed,
struct snd_emux *emu;
port = p;
- snd_assert(port != NULL, return);
- snd_assert(chset != NULL, return);
+ if (snd_BUG_ON(!port || !chset))
+ return;
emu = port->emu;
switch (parsed) {
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index f60a98ef7de..5c47b6c0926 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -114,7 +114,8 @@ snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
char tmpname[64];
emu = closure;
- snd_assert(arg != NULL && emu != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg || !emu))
+ return -ENXIO;
mutex_lock(&emu->register_mutex);
@@ -183,12 +184,15 @@ snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg)
struct snd_emux *emu;
struct snd_emux_port *p;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
p = arg->private_data;
- snd_assert(p != NULL, return -ENXIO);
+ if (snd_BUG_ON(!p))
+ return -ENXIO;
emu = p->emu;
- snd_assert(emu != NULL, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
mutex_lock(&emu->register_mutex);
snd_emux_sounds_off_all(p);
@@ -212,12 +216,15 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
struct snd_emux_port *p;
int rc;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
p = arg->private_data;
- snd_assert(p != NULL, return -ENXIO);
+ if (snd_BUG_ON(!p))
+ return -ENXIO;
emu = p->emu;
- snd_assert(emu != NULL, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
if (format == GUS_PATCH)
rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
@@ -252,12 +259,15 @@ snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned l
struct snd_emux_port *p;
struct snd_emux *emu;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
p = arg->private_data;
- snd_assert(p != NULL, return -ENXIO);
+ if (snd_BUG_ON(!p))
+ return -ENXIO;
emu = p->emu;
- snd_assert(emu != NULL, return -ENXIO);
+ if (snd_BUG_ON(!emu))
+ return -ENXIO;
switch (cmd) {
case SNDCTL_SEQ_RESETSAMPLES:
@@ -282,9 +292,11 @@ snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg)
{
struct snd_emux_port *p;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
p = arg->private_data;
- snd_assert(p != NULL, return -ENXIO);
+ if (snd_BUG_ON(!p))
+ return -ENXIO;
snd_emux_reset_port(p);
return 0;
}
@@ -302,9 +314,11 @@ snd_emux_event_oss_input(struct snd_seq_event *ev, int direct, void *private_dat
unsigned char cmd, *data;
p = private_data;
- snd_assert(p != NULL, return -EINVAL);
+ if (snd_BUG_ON(!p))
+ return -EINVAL;
emu = p->emu;
- snd_assert(emu != NULL, return -EINVAL);
+ if (snd_BUG_ON(!emu))
+ return -EINVAL;
if (ev->type != SNDRV_SEQ_EVENT_OSS)
return snd_emux_event_input(ev, direct, private_data, atomic, hop);
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
index d176cc01742..335aa2ce257 100644
--- a/sound/synth/emux/emux_seq.c
+++ b/sound/synth/emux/emux_seq.c
@@ -257,7 +257,8 @@ snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data,
struct snd_emux_port *port;
port = private_data;
- snd_assert(port != NULL && ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!port || !ev))
+ return -EINVAL;
snd_midi_process_event(&emux_ops, ev, &port->chset);
@@ -308,9 +309,11 @@ snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info)
struct snd_emux *emu;
p = private_data;
- snd_assert(p != NULL, return -EINVAL);
+ if (snd_BUG_ON(!p))
+ return -EINVAL;
emu = p->emu;
- snd_assert(emu != NULL, return -EINVAL);
+ if (snd_BUG_ON(!emu))
+ return -EINVAL;
mutex_lock(&emu->register_mutex);
snd_emux_init_port(p);
@@ -329,9 +332,11 @@ snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info)
struct snd_emux *emu;
p = private_data;
- snd_assert(p != NULL, return -EINVAL);
+ if (snd_BUG_ON(!p))
+ return -EINVAL;
emu = p->emu;
- snd_assert(emu != NULL, return -EINVAL);
+ if (snd_BUG_ON(!emu))
+ return -EINVAL;
mutex_lock(&emu->register_mutex);
snd_emux_sounds_off_all(p);
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index b343818dbb9..2cc6f6f7906 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -66,12 +66,12 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
struct snd_emux_port *port;
port = p;
- snd_assert(port != NULL && chan != NULL, return);
+ if (snd_BUG_ON(!port || !chan))
+ return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.get_voice != NULL, return);
- snd_assert(emu->ops.trigger != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger))
+ return;
key = note; /* remember the original note */
nvoices = get_zone(emu, port, &note, vel, chan, table);
@@ -164,11 +164,12 @@ snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
struct snd_emux_port *port;
port = p;
- snd_assert(port != NULL && chan != NULL, return);
+ if (snd_BUG_ON(!port || !chan))
+ return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.release != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.release))
+ return;
spin_lock_irqsave(&emu->voice_lock, flags);
for (ch = 0; ch < emu->max_voices; ch++) {
@@ -242,11 +243,12 @@ snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
struct snd_emux_port *port;
port = p;
- snd_assert(port != NULL && chan != NULL, return);
+ if (snd_BUG_ON(!port || !chan))
+ return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.update != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.update))
+ return;
spin_lock_irqsave(&emu->voice_lock, flags);
for (ch = 0; ch < emu->max_voices; ch++) {
@@ -276,8 +278,8 @@ snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *cha
return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.update != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.update))
+ return;
spin_lock_irqsave(&emu->voice_lock, flags);
for (i = 0; i < emu->max_voices; i++) {
@@ -303,8 +305,8 @@ snd_emux_update_port(struct snd_emux_port *port, int update)
return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.update != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.update))
+ return;
spin_lock_irqsave(&emu->voice_lock, flags);
for (i = 0; i < emu->max_voices; i++) {
@@ -326,7 +328,8 @@ snd_emux_control(void *p, int type, struct snd_midi_channel *chan)
struct snd_emux_port *port;
port = p;
- snd_assert(port != NULL && chan != NULL, return);
+ if (snd_BUG_ON(!port || !chan))
+ return;
switch (type) {
case MIDI_CTL_MSB_MAIN_VOLUME:
@@ -400,11 +403,12 @@ snd_emux_terminate_note(void *p, int note, struct snd_midi_channel *chan)
struct snd_emux_port *port;
port = p;
- snd_assert(port != NULL && chan != NULL, return);
+ if (snd_BUG_ON(!port || !chan))
+ return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.terminate != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.terminate))
+ return;
terminate_note1(emu, note, chan, 1);
}
@@ -451,10 +455,11 @@ snd_emux_sounds_off_all(struct snd_emux_port *port)
struct snd_emux_voice *vp;
unsigned long flags;
- snd_assert(port != NULL, return);
+ if (snd_BUG_ON(!port))
+ return;
emu = port->emu;
- snd_assert(emu != NULL, return);
- snd_assert(emu->ops.terminate != NULL, return);
+ if (snd_BUG_ON(!emu || !emu->ops.terminate))
+ return;
spin_lock_irqsave(&emu->voice_lock, flags);
for (i = 0; i < emu->max_voices; i++) {
diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c
index deabe5f899c..c85522e3808 100644
--- a/sound/synth/util_mem.c
+++ b/sound/synth/util_mem.c
@@ -55,7 +55,8 @@ void snd_util_memhdr_free(struct snd_util_memhdr *hdr)
{
struct list_head *p;
- snd_assert(hdr != NULL, return);
+ if (!hdr)
+ return;
/* release all blocks */
while ((p = hdr->block.next) != &hdr->block) {
list_del(p);
@@ -74,8 +75,8 @@ __snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
unsigned int units, prev_offset;
struct list_head *p;
- snd_assert(hdr != NULL, return NULL);
- snd_assert(size > 0, return NULL);
+ if (snd_BUG_ON(!hdr || size <= 0))
+ return NULL;
/* word alignment */
units = size;
@@ -161,7 +162,8 @@ __snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
*/
int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
{
- snd_assert(hdr && blk, return -EINVAL);
+ if (snd_BUG_ON(!hdr || !blk))
+ return -EINVAL;
mutex_lock(&hdr->block_mutex);
__snd_util_mem_free(hdr, blk);
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 9351b8a765b..4f0eac9bff1 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -1,11 +1,16 @@
# ALSA USB drivers
-menu "USB devices"
- depends on SND!=n && USB!=n
+menuconfig SND_USB
+ bool "USB sound devices"
+ depends on USB
+ default y
+ help
+ Support for sound devices connected via the USB bus.
+
+if SND_USB && USB
config SND_USB_AUDIO
tristate "USB Audio/MIDI driver"
- depends on SND && USB
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
@@ -18,7 +23,7 @@ config SND_USB_AUDIO
config SND_USB_USX2Y
tristate "Tascam US-122, US-224 and US-428 USB driver"
- depends on SND && USB && (X86 || PPC || ALPHA)
+ depends on X86 || PPC || ALPHA
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
@@ -31,7 +36,6 @@ 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
@@ -63,5 +67,17 @@ config SND_USB_CAIAQ_INPUT
* Native Instruments Kore Controller 2
* Native Instruments Audio Kontrol 1
-endmenu
+config SND_USB_US122L
+ tristate "Tascam US-122L USB driver"
+ depends on X86 && EXPERIMENTAL
+ select SND_HWDEP
+ select SND_RAWMIDI
+ help
+ Say Y here to include support for Tascam US-122L USB Audio/MIDI
+ interfaces.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-us122l.
+
+endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index aa252ef2ebf..abb288bfe35 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -8,5 +8,6 @@ snd-usb-lib-objs := usbmidi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o
obj-$(CONFIG_SND) += usx2y/ caiaq/
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
index 24970a5c888..b3a60332583 100644
--- a/sound/usb/caiaq/caiaq-audio.c
+++ b/sound/usb/caiaq/caiaq-audio.c
@@ -637,6 +637,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO):
dev->samplerates |= SNDRV_PCM_RATE_88200;
dev->samplerates |= SNDRV_PCM_RATE_192000;
break;
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index a972f77bd78..83175083e50 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -42,14 +42,15 @@
#endif
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.6");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.8");
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}}");
+ "{Native Instruments, Audio Kontrol 1},"
+ "{Native Instruments, Audio 8 DJ},"
+ "{Native Instruments, Session I/O}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -110,6 +111,11 @@ static struct usb_device_id snd_usb_id_table[] = {
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_AUDIO8DJ
},
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_SESSIONIO
+ },
{ /* terminator */ }
};
diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h
index 96a491379c6..f9fbdbae269 100644
--- a/sound/usb/caiaq/caiaq-device.h
+++ b/sound/usb/caiaq/caiaq-device.h
@@ -11,6 +11,7 @@
#define USB_PID_KORECONTROLLER2 0x4712
#define USB_PID_AK1 0x0815
#define USB_PID_AUDIO8DJ 0x1978
+#define USB_PID_SESSIONIO 0x1915
#define EP1_BUFSIZE 64
#define CAIAQ_USB_STR_LEN 0xff
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 410be4aff1b..bbd70d5814a 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -71,6 +71,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
static int nrpacks = 8; /* max. number of packets per urb */
static int async_unlink = 1;
static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/
+static int ignore_ctl_error;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -88,7 +89,9 @@ module_param(async_unlink, bool, 0444);
MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
module_param_array(device_setup, int, NULL, 0444);
MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
-
+module_param(ignore_ctl_error, bool, 0444);
+MODULE_PARM_DESC(ignore_ctl_error,
+ "Ignore errors from USB controller for mixer interfaces.");
/*
* debug the h/w constraints
@@ -481,7 +484,7 @@ static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
}
/*
- * process after E-Mu 0202/0404 high speed playback sync complete
+ * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
*
* These devices return the number of samples per packet instead of the number
* of samples per microframe.
@@ -819,10 +822,6 @@ static const char *usb_error_string(int err)
return "device disabled";
case -EHOSTUNREACH:
return "device suspended";
-#ifndef CONFIG_USB_EHCI_SPLIT_ISO
- case -ENOSYS:
- return "enable CONFIG_USB_EHCI_SPLIT_ISO to play through a hub";
-#endif
case -EINVAL:
case -EAGAIN:
case -EFBIG:
@@ -845,7 +844,8 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
return -EBADFD;
for (i = 0; i < subs->nurbs; i++) {
- snd_assert(subs->dataurb[i].urb, return -EINVAL);
+ if (snd_BUG_ON(!subs->dataurb[i].urb))
+ return -EINVAL;
if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
goto __error;
@@ -853,7 +853,8 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru
}
if (subs->syncpipe) {
for (i = 0; i < SYNC_URBS; i++) {
- snd_assert(subs->syncurb[i].urb, return -EINVAL);
+ if (snd_BUG_ON(!subs->syncurb[i].urb))
+ return -EINVAL;
if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
goto __error;
@@ -1325,10 +1326,12 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
int err;
iface = usb_ifnum_to_if(dev, fmt->iface);
- snd_assert(iface, return -EINVAL);
+ if (WARN_ON(!iface))
+ return -EINVAL;
alts = &iface->altsetting[fmt->altset_idx];
altsd = get_iface_desc(alts);
- snd_assert(altsd->bAlternateSetting == fmt->altsetting, return -EINVAL);
+ if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
+ return -EINVAL;
if (fmt == subs->cur_audiofmt)
return 0;
@@ -2261,6 +2264,7 @@ static void init_substream(struct snd_usb_stream *as, int stream, struct audiofo
switch (as->chip->usb_id) {
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+ case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
break;
}
@@ -2993,12 +2997,12 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
}
/*
- * Create a stream for an Edirol UA-700/UA-25 interface. The only way
- * to detect the sample rate is by looking at wMaxPacketSize.
+ * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
+ * The only way to detect the sample rate is by looking at wMaxPacketSize.
*/
-static int create_ua700_ua25_quirk(struct snd_usb_audio *chip,
- struct usb_interface *iface,
- const struct snd_usb_audio_quirk *quirk)
+static int create_uaxx_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ const struct snd_usb_audio_quirk *quirk)
{
static const struct audioformat ua_format = {
.format = SNDRV_PCM_FORMAT_S24_3LE,
@@ -3013,8 +3017,8 @@ static int create_ua700_ua25_quirk(struct snd_usb_audio *chip,
struct audioformat *fp;
int stream, err;
- /* both PCM and MIDI interfaces have 2 altsettings */
- if (iface->num_altsetting != 2)
+ /* both PCM and MIDI interfaces have 2 or more altsettings */
+ if (iface->num_altsetting < 2)
return -ENXIO;
alts = &iface->altsetting[1];
altsd = get_iface_desc(alts);
@@ -3028,20 +3032,20 @@ static int create_ua700_ua25_quirk(struct snd_usb_audio *chip,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = &ua700_ep
};
- static const struct snd_usb_midi_endpoint_info ua25_ep = {
+ static const struct snd_usb_midi_endpoint_info uaxx_ep = {
.out_cables = 0x0001,
.in_cables = 0x0001
};
- static const struct snd_usb_audio_quirk ua25_quirk = {
+ static const struct snd_usb_audio_quirk uaxx_quirk = {
.type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = &ua25_ep
+ .data = &uaxx_ep
};
if (chip->usb_id == USB_ID(0x0582, 0x002b))
return snd_usb_create_midi_interface(chip, iface,
&ua700_quirk);
else
return snd_usb_create_midi_interface(chip, iface,
- &ua25_quirk);
+ &uaxx_quirk);
}
if (altsd->bNumEndpoints != 1)
@@ -3373,9 +3377,9 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,
[QUIRK_MIDI_CME] = snd_usb_create_midi_interface,
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
- [QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
[QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk,
+ [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk
};
if (quirk->type < QUIRK_TYPE_COUNT) {
@@ -3633,7 +3637,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
if (err > 0) {
/* create normal USB audio interfaces */
if (snd_usb_create_streams(chip, ifnum) < 0 ||
- snd_usb_create_mixer(chip, ifnum) < 0) {
+ snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {
goto __error;
}
}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 7cf18c38dc4..36e4f7a29ad 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -156,11 +156,12 @@ enum quirk_type {
QUIRK_MIDI_RAW,
QUIRK_MIDI_EMAGIC,
QUIRK_MIDI_CME,
+ QUIRK_MIDI_US122L,
QUIRK_AUDIO_STANDARD_INTERFACE,
QUIRK_AUDIO_FIXED_ENDPOINT,
- QUIRK_AUDIO_EDIROL_UA700_UA25,
QUIRK_AUDIO_EDIROL_UA1000,
QUIRK_AUDIO_EDIROL_UA101,
+ QUIRK_AUDIO_EDIROL_UAXX,
QUIRK_TYPE_COUNT
};
@@ -222,7 +223,8 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
void *data, __u16 size, int timeout);
-int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif);
+int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
+ int ignore_error);
void snd_usb_mixer_disconnect(struct list_head *p);
int snd_usb_create_midi_interface(struct snd_usb_audio *chip, struct usb_interface *iface,
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 6676a177c99..5962e4b8442 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -669,6 +669,42 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = {
.output = snd_usbmidi_raw_output,
};
+static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,
+ uint8_t *buffer, int buffer_length)
+{
+ if (buffer_length != 9)
+ return;
+ buffer_length = 8;
+ while (buffer_length && buffer[buffer_length - 1] == 0xFD)
+ buffer_length--;
+ if (buffer_length)
+ snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
+}
+
+static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep)
+{
+ int count;
+
+ if (!ep->ports[0].active)
+ return;
+ count = ep->urb->dev->speed == USB_SPEED_HIGH ? 1 : 2;
+ count = snd_rawmidi_transmit(ep->ports[0].substream,
+ ep->urb->transfer_buffer,
+ count);
+ if (count < 1) {
+ ep->ports[0].active = 0;
+ return;
+ }
+
+ memset(ep->urb->transfer_buffer + count, 0xFD, 9 - count);
+ ep->urb->transfer_buffer_length = count;
+}
+
+static struct usb_protocol_ops snd_usbmidi_122l_ops = {
+ .input = snd_usbmidi_us122l_input,
+ .output = snd_usbmidi_us122l_output,
+};
+
/*
* Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching.
*/
@@ -1076,6 +1112,15 @@ void snd_usbmidi_disconnect(struct list_head* p)
}
if (ep->in)
usb_kill_urb(ep->in->urb);
+ /* free endpoints here; later call can result in Oops */
+ if (ep->out) {
+ snd_usbmidi_out_endpoint_delete(ep->out);
+ ep->out = NULL;
+ }
+ if (ep->in) {
+ snd_usbmidi_in_endpoint_delete(ep->in);
+ ep->in = NULL;
+ }
}
del_timer_sync(&umidi->error_timer);
}
@@ -1714,6 +1759,9 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
umidi->usb_protocol_ops =
&snd_usbmidi_maudio_broken_running_status_ops;
break;
+ case QUIRK_MIDI_US122L:
+ umidi->usb_protocol_ops = &snd_usbmidi_122l_ops;
+ /* fall through */
case QUIRK_MIDI_FIXED_ENDPOINT:
memcpy(&endpoints[0], quirk->data,
sizeof(struct snd_usb_midi_endpoint_info));
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 89c63d073cc..a49246113e7 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -59,12 +59,13 @@ static const struct rc_config {
u8 offset;
u8 length;
u8 packet_length;
+ u8 min_packet_length; /* minimum accepted length of the URB result */
u8 mute_mixer_id;
u32 mute_code;
} rc_configs[] = {
- { USB_ID(0x041e, 0x3000), 0, 1, 2, 18, 0x0013 }, /* Extigy */
- { USB_ID(0x041e, 0x3020), 2, 1, 6, 18, 0x0013 }, /* Audigy 2 NX */
- { USB_ID(0x041e, 0x3040), 2, 2, 6, 2, 0x6e91 }, /* Live! 24-bit */
+ { USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */
+ { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
+ { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
};
struct usb_mixer_interface {
@@ -1388,7 +1389,8 @@ static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl
struct usb_mixer_elem_info *cval = kcontrol->private_data;
char **itemlist = (char **)kcontrol->private_value;
- snd_assert(itemlist, return -EINVAL);
+ if (snd_BUG_ON(!itemlist))
+ return -EINVAL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = cval->max;
@@ -1781,7 +1783,7 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb)
const struct rc_config *rc = mixer->rc_cfg;
u32 code;
- if (urb->status < 0 || urb->actual_length < rc->packet_length)
+ if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
return;
code = mixer->rc_buffer[rc->offset];
@@ -2012,7 +2014,8 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
}
}
-int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
+int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
+ int ignore_error)
{
static struct snd_device_ops dev_ops = {
.dev_free = snd_usb_mixer_dev_free
@@ -2027,9 +2030,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
return -ENOMEM;
mixer->chip = chip;
mixer->ctrlif = ctrlif;
-#ifdef IGNORE_CTL_ERROR
- mixer->ignore_ctl_error = 1;
-#endif
+ mixer->ignore_ctl_error = ignore_error;
mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL);
if (!mixer->id_elems) {
kfree(mixer);
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 82a8d14c26a..69689e79bf7 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -62,6 +62,13 @@
.idProduct = 0x3f04,
.bInterfaceClass = USB_CLASS_AUDIO,
},
+{
+ /* E-Mu Tracker Pre */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x041e,
+ .idProduct = 0x3f0a,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+},
/*
* Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface
@@ -210,6 +217,11 @@ YAMAHA_DEVICE(0x1042, NULL),
YAMAHA_DEVICE(0x1043, NULL),
YAMAHA_DEVICE(0x1044, NULL),
YAMAHA_DEVICE(0x1045, NULL),
+YAMAHA_INTERFACE(0x104e, 0, NULL),
+YAMAHA_DEVICE(0x104f, NULL),
+YAMAHA_DEVICE(0x1050, NULL),
+YAMAHA_DEVICE(0x1051, NULL),
+YAMAHA_DEVICE(0x1052, NULL),
YAMAHA_DEVICE(0x2000, "DGP-7"),
YAMAHA_DEVICE(0x2001, "DGP-5"),
YAMAHA_DEVICE(0x2002, NULL),
@@ -850,15 +862,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UA700_UA25
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UA700_UA25
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = 3,
- .type = QUIRK_AUDIO_EDIROL_UA700_UA25
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = -1
@@ -1192,15 +1204,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_AUDIO_EDIROL_UA700_UA25
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UA700_UA25
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UA700_UA25
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = -1
@@ -1333,6 +1345,36 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ /*
+ * This quirk is for the "Advanced Driver" mode. If off, the UA-4FX
+ * is standard compliant, but has only 16-bit PCM and no MIDI.
+ */
+ USB_DEVICE(0x0582, 0x00a3),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "EDIROL",
+ .product_name = "UA-4FX",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_EDIROL_UAXX
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_EDIROL_UAXX
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_EDIROL_UAXX
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* TODO: add Edirol MD-P1 support */
{
USB_DEVICE(0x582, 0x00a6),
@@ -1378,6 +1420,66 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ /* Roland SonicCell */
+ USB_DEVICE(0x0582, 0x00c2),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Roland",
+ .product_name = "SonicCell",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ /* BOSS GT-10 */
+ USB_DEVICE(0x0582, 0x00da),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile
index 9ac22bce112..748933054b6 100644
--- a/sound/usb/usx2y/Makefile
+++ b/sound/usb/usx2y/Makefile
@@ -1,3 +1,5 @@
snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
+snd-usb-us122l-objs := us122l.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usb-us122l.o
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
new file mode 100644
index 00000000000..c2515b680f9
--- /dev/null
+++ b/sound/usb/usx2y/us122l.c
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sound/core.h>
+#include <sound/hwdep.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#define MODNAME "US122L"
+#include "usb_stream.c"
+#include "../usbaudio.h"
+#include "us122l.h"
+
+MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>");
+MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.5");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
+ /* Enable this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");
+
+static int snd_us122l_card_used[SNDRV_CARDS];
+
+
+static int us122l_create_usbmidi(struct snd_card *card)
+{
+ static struct snd_usb_midi_endpoint_info quirk_data = {
+ .out_ep = 4,
+ .in_ep = 3,
+ .out_cables = 0x001,
+ .in_cables = 0x001
+ };
+ static struct snd_usb_audio_quirk quirk = {
+ .vendor_name = "US122L",
+ .product_name = NAME_ALLCAPS,
+ .ifnum = 1,
+ .type = QUIRK_MIDI_US122L,
+ .data = &quirk_data
+ };
+ struct usb_device *dev = US122L(card)->chip.dev;
+ struct usb_interface *iface = usb_ifnum_to_if(dev, 1);
+
+ return snd_usb_create_midi_interface(&US122L(card)->chip,
+ iface, &quirk);
+}
+
+/*
+ * Wrapper for usb_control_msg().
+ * Allocates a temp buffer to prevent dmaing from/to the stack.
+ */
+static int us122l_ctl_msg(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype,
+ __u16 value, __u16 index, void *data,
+ __u16 size, int timeout)
+{
+ int err;
+ void *buf = NULL;
+
+ if (size > 0) {
+ buf = kmemdup(data, size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ }
+ err = usb_control_msg(dev, pipe, request, requesttype,
+ value, index, buf, size, timeout);
+ if (size > 0) {
+ memcpy(data, buf, size);
+ kfree(buf);
+ }
+ return err;
+}
+
+static void pt_info_set(struct usb_device *dev, u8 v)
+{
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 'I',
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ v, 0, NULL, 0, 1000);
+ snd_printdd(KERN_DEBUG "%i\n", ret);
+}
+
+static void usb_stream_hwdep_vm_open(struct vm_area_struct *area)
+{
+ struct us122l *us122l = area->vm_private_data;
+ atomic_inc(&us122l->mmap_count);
+ snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
+}
+
+static int usb_stream_hwdep_vm_fault(struct vm_area_struct *area,
+ struct vm_fault *vmf)
+{
+ unsigned long offset;
+ struct page *page;
+ void *vaddr;
+ struct us122l *us122l = area->vm_private_data;
+ struct usb_stream *s;
+
+ mutex_lock(&us122l->mutex);
+ s = us122l->sk.s;
+ if (!s)
+ goto unlock;
+
+ offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset < PAGE_ALIGN(s->read_size))
+ vaddr = (char *)s + offset;
+ else {
+ offset -= PAGE_ALIGN(s->read_size);
+ if (offset >= PAGE_ALIGN(s->write_size))
+ goto unlock;
+
+ vaddr = us122l->sk.write_page + offset;
+ }
+ page = virt_to_page(vaddr);
+
+ get_page(page);
+ mutex_unlock(&us122l->mutex);
+
+ vmf->page = page;
+
+ return 0;
+unlock:
+ mutex_unlock(&us122l->mutex);
+ return VM_FAULT_SIGBUS;
+}
+
+static void usb_stream_hwdep_vm_close(struct vm_area_struct *area)
+{
+ struct us122l *us122l = area->vm_private_data;
+ atomic_dec(&us122l->mmap_count);
+ snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
+}
+
+static struct vm_operations_struct usb_stream_hwdep_vm_ops = {
+ .open = usb_stream_hwdep_vm_open,
+ .fault = usb_stream_hwdep_vm_fault,
+ .close = usb_stream_hwdep_vm_close,
+};
+
+
+static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ struct us122l *us122l = hw->private_data;
+ struct usb_interface *iface;
+ snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
+ if (hw->used >= 2)
+ return -EBUSY;
+
+ if (!us122l->first)
+ us122l->first = file;
+ iface = usb_ifnum_to_if(us122l->chip.dev, 1);
+ usb_autopm_get_interface(iface);
+ return 0;
+}
+
+static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct us122l *us122l = hw->private_data;
+ struct usb_interface *iface = usb_ifnum_to_if(us122l->chip.dev, 1);
+ snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
+ usb_autopm_put_interface(iface);
+ if (us122l->first == file)
+ us122l->first = NULL;
+ mutex_lock(&us122l->mutex);
+ if (us122l->master == file)
+ us122l->master = us122l->slave;
+
+ us122l->slave = NULL;
+ mutex_unlock(&us122l->mutex);
+ return 0;
+}
+
+static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
+ struct file *filp, struct vm_area_struct *area)
+{
+ unsigned long size = area->vm_end - area->vm_start;
+ struct us122l *us122l = hw->private_data;
+ unsigned long offset;
+ struct usb_stream *s;
+ int err = 0;
+ bool read;
+
+ offset = area->vm_pgoff << PAGE_SHIFT;
+ mutex_lock(&us122l->mutex);
+ s = us122l->sk.s;
+ read = offset < s->read_size;
+ if (read && area->vm_flags & VM_WRITE) {
+ err = -EPERM;
+ goto out;
+ }
+ snd_printdd(KERN_DEBUG "%lu %u\n", size,
+ read ? s->read_size : s->write_size);
+ /* if userspace tries to mmap beyond end of our buffer, fail */
+ if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) {
+ snd_printk(KERN_WARNING "%lu > %u\n", size,
+ read ? s->read_size : s->write_size);
+ err = -EINVAL;
+ goto out;
+ }
+
+ area->vm_ops = &usb_stream_hwdep_vm_ops;
+ area->vm_flags |= VM_RESERVED;
+ area->vm_private_data = us122l;
+ atomic_inc(&us122l->mmap_count);
+out:
+ mutex_unlock(&us122l->mutex);
+ return err;
+}
+
+static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
+ struct file *file, poll_table *wait)
+{
+ struct us122l *us122l = hw->private_data;
+ struct usb_stream *s = us122l->sk.s;
+ unsigned *polled;
+ unsigned int mask;
+
+ poll_wait(file, &us122l->sk.sleep, wait);
+
+ switch (s->state) {
+ case usb_stream_ready:
+ if (us122l->first == file)
+ polled = &s->periods_polled;
+ else
+ polled = &us122l->second_periods_polled;
+ if (*polled != s->periods_done) {
+ *polled = s->periods_done;
+ mask = POLLIN | POLLOUT | POLLWRNORM;
+ break;
+ }
+ /* Fall through */
+ mask = 0;
+ break;
+ default:
+ mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
+ break;
+ }
+ return mask;
+}
+
+static void us122l_stop(struct us122l *us122l)
+{
+ struct list_head *p;
+ list_for_each(p, &us122l->chip.midi_list)
+ snd_usbmidi_input_stop(p);
+
+ usb_stream_stop(&us122l->sk);
+ usb_stream_free(&us122l->sk);
+}
+
+static int us122l_set_sample_rate(struct usb_device *dev, int rate)
+{
+ unsigned int ep = 0x81;
+ unsigned char data[3];
+ int err;
+
+ data[0] = rate;
+ data[1] = rate >> 8;
+ data[2] = rate >> 16;
+ err = us122l_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,
+ USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+ SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000);
+ if (err < 0)
+ snd_printk(KERN_ERR "%d: cannot set freq %d to ep 0x%x\n",
+ dev->devnum, rate, ep);
+ return err;
+}
+
+static bool us122l_start(struct us122l *us122l,
+ unsigned rate, unsigned period_frames)
+{
+ struct list_head *p;
+ int err;
+ unsigned use_packsize = 0;
+ bool success = false;
+
+ if (us122l->chip.dev->speed == USB_SPEED_HIGH) {
+ /* The us-122l's descriptor defaults to iso max_packsize 78,
+ which isn't needed for samplerates <= 48000.
+ Lets save some memory:
+ */
+ switch (rate) {
+ case 44100:
+ use_packsize = 36;
+ break;
+ case 48000:
+ use_packsize = 42;
+ break;
+ case 88200:
+ use_packsize = 72;
+ break;
+ }
+ }
+ if (!usb_stream_new(&us122l->sk, us122l->chip.dev, 1, 2,
+ rate, use_packsize, period_frames, 6))
+ goto out;
+
+ err = us122l_set_sample_rate(us122l->chip.dev, rate);
+ if (err < 0) {
+ us122l_stop(us122l);
+ snd_printk(KERN_ERR "us122l_set_sample_rate error \n");
+ goto out;
+ }
+ err = usb_stream_start(&us122l->sk);
+ if (err < 0) {
+ us122l_stop(us122l);
+ snd_printk(KERN_ERR "us122l_start error %i \n", err);
+ goto out;
+ }
+ list_for_each(p, &us122l->chip.midi_list)
+ snd_usbmidi_input_start(p);
+ success = true;
+out:
+ return success;
+}
+
+static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned cmd, unsigned long arg)
+{
+ struct usb_stream_config *cfg;
+ struct us122l *us122l = hw->private_data;
+ unsigned min_period_frames;
+ int err = 0;
+ bool high_speed;
+
+ if (cmd != SNDRV_USB_STREAM_IOCTL_SET_PARAMS)
+ return -ENOTTY;
+
+ cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ if (copy_from_user(cfg, (void *)arg, sizeof(*cfg))) {
+ err = -EFAULT;
+ goto free;
+ }
+ if (cfg->version != USB_STREAM_INTERFACE_VERSION) {
+ err = -ENXIO;
+ goto free;
+ }
+ high_speed = us122l->chip.dev->speed == USB_SPEED_HIGH;
+ if ((cfg->sample_rate != 44100 && cfg->sample_rate != 48000 &&
+ (!high_speed ||
+ (cfg->sample_rate != 88200 && cfg->sample_rate != 96000))) ||
+ cfg->frame_size != 6 ||
+ cfg->period_frames > 0x3000) {
+ err = -EINVAL;
+ goto free;
+ }
+ switch (cfg->sample_rate) {
+ case 44100:
+ min_period_frames = 48;
+ break;
+ case 48000:
+ min_period_frames = 52;
+ break;
+ default:
+ min_period_frames = 104;
+ break;
+ }
+ if (!high_speed)
+ min_period_frames <<= 1;
+ if (cfg->period_frames < min_period_frames) {
+ err = -EINVAL;
+ goto free;
+ }
+
+ snd_power_wait(hw->card, SNDRV_CTL_POWER_D0);
+
+ mutex_lock(&us122l->mutex);
+ if (!us122l->master)
+ us122l->master = file;
+ else if (us122l->master != file) {
+ if (memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg))) {
+ err = -EIO;
+ goto unlock;
+ }
+ us122l->slave = file;
+ }
+ if (!us122l->sk.s ||
+ memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) ||
+ us122l->sk.s->state == usb_stream_xrun) {
+ us122l_stop(us122l);
+ if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames))
+ err = -EIO;
+ else
+ err = 1;
+ }
+unlock:
+ mutex_unlock(&us122l->mutex);
+free:
+ kfree(cfg);
+ return err;
+}
+
+#define SND_USB_STREAM_ID "USB STREAM"
+static int usb_stream_hwdep_new(struct snd_card *card)
+{
+ int err;
+ struct snd_hwdep *hw;
+ struct usb_device *dev = US122L(card)->chip.dev;
+
+ err = snd_hwdep_new(card, SND_USB_STREAM_ID, 0, &hw);
+ if (err < 0)
+ return err;
+
+ hw->iface = SNDRV_HWDEP_IFACE_USB_STREAM;
+ hw->private_data = US122L(card);
+ hw->ops.open = usb_stream_hwdep_open;
+ hw->ops.release = usb_stream_hwdep_release;
+ hw->ops.ioctl = usb_stream_hwdep_ioctl;
+ hw->ops.ioctl_compat = usb_stream_hwdep_ioctl;
+ hw->ops.mmap = usb_stream_hwdep_mmap;
+ hw->ops.poll = usb_stream_hwdep_poll;
+
+ sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm",
+ dev->bus->busnum, dev->devnum);
+ return 0;
+}
+
+
+static bool us122l_create_card(struct snd_card *card)
+{
+ int err;
+ struct us122l *us122l = US122L(card);
+
+ err = usb_set_interface(us122l->chip.dev, 1, 1);
+ if (err) {
+ snd_printk(KERN_ERR "usb_set_interface error \n");
+ return false;
+ }
+
+ pt_info_set(us122l->chip.dev, 0x11);
+ pt_info_set(us122l->chip.dev, 0x10);
+
+ if (!us122l_start(us122l, 44100, 256))
+ return false;
+
+ err = us122l_create_usbmidi(card);
+ if (err < 0) {
+ snd_printk(KERN_ERR "us122l_create_usbmidi error %i \n", err);
+ us122l_stop(us122l);
+ return false;
+ }
+ err = usb_stream_hwdep_new(card);
+ if (err < 0) {
+/* release the midi resources */
+ struct list_head *p;
+ list_for_each(p, &us122l->chip.midi_list)
+ snd_usbmidi_disconnect(p);
+
+ us122l_stop(us122l);
+ return false;
+ }
+ return true;
+}
+
+static struct snd_card *usx2y_create_card(struct usb_device *device)
+{
+ int dev;
+ struct snd_card *card;
+ for (dev = 0; dev < SNDRV_CARDS; ++dev)
+ if (enable[dev] && !snd_us122l_card_used[dev])
+ break;
+ if (dev >= SNDRV_CARDS)
+ return NULL;
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct us122l));
+ if (!card)
+ return NULL;
+ snd_us122l_card_used[US122L(card)->chip.index = dev] = 1;
+
+ US122L(card)->chip.dev = device;
+ US122L(card)->chip.card = card;
+ mutex_init(&US122L(card)->mutex);
+ init_waitqueue_head(&US122L(card)->sk.sleep);
+ INIT_LIST_HEAD(&US122L(card)->chip.midi_list);
+ strcpy(card->driver, "USB "NAME_ALLCAPS"");
+ sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
+ sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
+ card->shortname,
+ le16_to_cpu(device->descriptor.idVendor),
+ le16_to_cpu(device->descriptor.idProduct),
+ 0,
+ US122L(card)->chip.dev->bus->busnum,
+ US122L(card)->chip.dev->devnum
+ );
+ snd_card_set_dev(card, &device->dev);
+ return card;
+}
+
+static void *us122l_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *device_id)
+{
+ struct usb_device *device = interface_to_usbdev(intf);
+ struct snd_card *card = usx2y_create_card(device);
+
+ if (!card)
+ return NULL;
+
+ if (!us122l_create_card(card) ||
+ snd_card_register(card) < 0) {
+ snd_card_free(card);
+ return NULL;
+ }
+
+ usb_get_dev(device);
+ return card;
+}
+
+static int snd_us122l_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct snd_card *card;
+ snd_printdd(KERN_DEBUG"%p:%i\n",
+ intf, intf->cur_altsetting->desc.bInterfaceNumber);
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
+ return 0;
+
+ card = us122l_usb_probe(usb_get_intf(intf), id);
+
+ if (card) {
+ usb_set_intfdata(intf, card);
+ return 0;
+ }
+
+ usb_put_intf(intf);
+ return -EIO;
+}
+
+static void snd_us122l_disconnect(struct usb_interface *intf)
+{
+ struct snd_card *card;
+ struct us122l *us122l;
+ struct list_head *p;
+
+ card = usb_get_intfdata(intf);
+ if (!card)
+ return;
+
+ snd_card_disconnect(card);
+
+ us122l = US122L(card);
+ mutex_lock(&us122l->mutex);
+ us122l_stop(us122l);
+ mutex_unlock(&us122l->mutex);
+ us122l->chip.shutdown = 1;
+
+/* release the midi resources */
+ list_for_each(p, &us122l->chip.midi_list) {
+ snd_usbmidi_disconnect(p);
+ }
+
+ usb_put_intf(intf);
+ usb_put_dev(US122L(card)->chip.dev);
+
+ while (atomic_read(&us122l->mmap_count))
+ msleep(500);
+
+ snd_card_free(card);
+}
+
+static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct snd_card *card;
+ struct us122l *us122l;
+ struct list_head *p;
+
+ card = dev_get_drvdata(&intf->dev);
+ if (!card)
+ return 0;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ us122l = US122L(card);
+ if (!us122l)
+ return 0;
+
+ list_for_each(p, &us122l->chip.midi_list)
+ snd_usbmidi_input_stop(p);
+
+ mutex_lock(&us122l->mutex);
+ usb_stream_stop(&us122l->sk);
+ mutex_unlock(&us122l->mutex);
+
+ return 0;
+}
+
+static int snd_us122l_resume(struct usb_interface *intf)
+{
+ struct snd_card *card;
+ struct us122l *us122l;
+ struct list_head *p;
+ int err;
+
+ card = dev_get_drvdata(&intf->dev);
+ if (!card)
+ return 0;
+
+ us122l = US122L(card);
+ if (!us122l)
+ return 0;
+
+ mutex_lock(&us122l->mutex);
+ /* needed, doesn't restart without: */
+ err = usb_set_interface(us122l->chip.dev, 1, 1);
+ if (err) {
+ snd_printk(KERN_ERR "usb_set_interface error \n");
+ goto unlock;
+ }
+
+ pt_info_set(us122l->chip.dev, 0x11);
+ pt_info_set(us122l->chip.dev, 0x10);
+
+ err = us122l_set_sample_rate(us122l->chip.dev,
+ us122l->sk.s->cfg.sample_rate);
+ if (err < 0) {
+ snd_printk(KERN_ERR "us122l_set_sample_rate error \n");
+ goto unlock;
+ }
+ err = usb_stream_start(&us122l->sk);
+ if (err)
+ goto unlock;
+
+ list_for_each(p, &us122l->chip.midi_list)
+ snd_usbmidi_input_start(p);
+unlock:
+ mutex_unlock(&us122l->mutex);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return err;
+}
+
+static struct usb_device_id snd_us122l_usb_id_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0644,
+ .idProduct = USB_ID_US122L
+ },
+/* { */ /* US-144 maybe works when @USB1.1. Untested. */
+/* .match_flags = USB_DEVICE_ID_MATCH_DEVICE, */
+/* .idVendor = 0x0644, */
+/* .idProduct = USB_ID_US144 */
+/* }, */
+ { /* terminator */ }
+};
+
+MODULE_DEVICE_TABLE(usb, snd_us122l_usb_id_table);
+static struct usb_driver snd_us122l_usb_driver = {
+ .name = "snd-usb-us122l",
+ .probe = snd_us122l_probe,
+ .disconnect = snd_us122l_disconnect,
+ .suspend = snd_us122l_suspend,
+ .resume = snd_us122l_resume,
+ .reset_resume = snd_us122l_resume,
+ .id_table = snd_us122l_usb_id_table,
+ .supports_autosuspend = 1
+};
+
+
+static int __init snd_us122l_module_init(void)
+{
+ return usb_register(&snd_us122l_usb_driver);
+}
+
+static void __exit snd_us122l_module_exit(void)
+{
+ usb_deregister(&snd_us122l_usb_driver);
+}
+
+module_init(snd_us122l_module_init)
+module_exit(snd_us122l_module_exit)
diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h
new file mode 100644
index 00000000000..3d10c4b2a0f
--- /dev/null
+++ b/sound/usb/usx2y/us122l.h
@@ -0,0 +1,27 @@
+#ifndef US122L_H
+#define US122L_H
+
+
+struct us122l {
+ struct snd_usb_audio chip;
+ int stride;
+ struct usb_stream_kernel sk;
+
+ struct mutex mutex;
+ struct file *first;
+ unsigned second_periods_polled;
+ struct file *master;
+ struct file *slave;
+
+ atomic_t mmap_count;
+};
+
+
+#define US122L(c) ((struct us122l *)(c)->private_data)
+
+#define NAME_ALLCAPS "US-122L"
+
+#define USB_ID_US122L 0x800E
+#define USB_ID_US144 0x800F
+
+#endif
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
new file mode 100644
index 00000000000..ff23cc1ce3b
--- /dev/null
+++ b/sound/usb/usx2y/usb_stream.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/usb.h>
+
+#include "usb_stream.h"
+
+
+/* setup */
+
+static unsigned usb_stream_next_packet_size(struct usb_stream_kernel *sk)
+{
+ struct usb_stream *s = sk->s;
+ sk->out_phase_peeked = (sk->out_phase & 0xffff) + sk->freqn;
+ return (sk->out_phase_peeked >> 16) * s->cfg.frame_size;
+}
+
+static void playback_prep_freqn(struct usb_stream_kernel *sk, struct urb *urb)
+{
+ struct usb_stream *s = sk->s;
+ unsigned l = 0;
+ int pack;
+
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = usb_stream_next_packet_size(sk);
+ sk->out_phase = sk->out_phase_peeked;
+ urb->transfer_buffer_length = urb->iso_frame_desc[0].length;
+
+ for (pack = 1; pack < sk->n_o_ps; pack++) {
+ l = usb_stream_next_packet_size(sk);
+ if (s->idle_outsize + urb->transfer_buffer_length + l >
+ s->period_size)
+ goto check;
+
+ sk->out_phase = sk->out_phase_peeked;
+ urb->iso_frame_desc[pack].offset = urb->transfer_buffer_length;
+ urb->iso_frame_desc[pack].length = l;
+ urb->transfer_buffer_length += l;
+ }
+ snd_printdd(KERN_DEBUG "%i\n", urb->transfer_buffer_length);
+
+check:
+ urb->number_of_packets = pack;
+ s->idle_outsize += urb->transfer_buffer_length - s->period_size;
+ snd_printdd(KERN_DEBUG "idle=%i ul=%i ps=%i\n", s->idle_outsize,
+ urb->transfer_buffer_length, s->period_size);
+}
+
+static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize,
+ struct urb **urbs, char *transfer,
+ struct usb_device *dev, int pipe)
+{
+ int u, p;
+ int maxpacket = use_packsize ?
+ use_packsize : usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+ int transfer_length = maxpacket * sk->n_o_ps;
+
+ for (u = 0; u < USB_STREAM_NURBS;
+ ++u, transfer += transfer_length) {
+ struct urb *urb = urbs[u];
+ struct usb_iso_packet_descriptor *desc;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = transfer;
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->number_of_packets = sk->n_o_ps;
+ urb->context = sk;
+ urb->interval = 1;
+ if (usb_pipeout(pipe))
+ continue;
+
+ urb->transfer_buffer_length = transfer_length;
+ desc = urb->iso_frame_desc;
+ desc->offset = 0;
+ desc->length = maxpacket;
+ for (p = 1; p < sk->n_o_ps; ++p) {
+ desc[p].offset = desc[p - 1].offset + maxpacket;
+ desc[p].length = maxpacket;
+ }
+ }
+}
+
+static void init_urbs(struct usb_stream_kernel *sk, unsigned use_packsize,
+ struct usb_device *dev, int in_pipe, int out_pipe)
+{
+ struct usb_stream *s = sk->s;
+ char *indata = (char *)s + sizeof(*s) +
+ sizeof(struct usb_stream_packet) *
+ s->inpackets;
+ int u;
+
+ for (u = 0; u < USB_STREAM_NURBS; ++u) {
+ sk->inurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL);
+ sk->outurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL);
+ }
+
+ init_pipe_urbs(sk, use_packsize, sk->inurb, indata, dev, in_pipe);
+ init_pipe_urbs(sk, use_packsize, sk->outurb, sk->write_page, dev,
+ out_pipe);
+}
+
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+static inline unsigned get_usb_full_speed_rate(unsigned rate)
+{
+ return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned rate)
+{
+ return ((rate << 10) + 62) / 125;
+}
+
+void usb_stream_free(struct usb_stream_kernel *sk)
+{
+ struct usb_stream *s;
+ unsigned u;
+
+ for (u = 0; u < USB_STREAM_NURBS; ++u) {
+ usb_free_urb(sk->inurb[u]);
+ sk->inurb[u] = NULL;
+ usb_free_urb(sk->outurb[u]);
+ sk->outurb[u] = NULL;
+ }
+
+ s = sk->s;
+ if (!s)
+ return;
+
+ free_pages((unsigned long)sk->write_page, get_order(s->write_size));
+ sk->write_page = NULL;
+ free_pages((unsigned long)s, get_order(s->read_size));
+ sk->s = NULL;
+}
+
+struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
+ struct usb_device *dev,
+ unsigned in_endpoint, unsigned out_endpoint,
+ unsigned sample_rate, unsigned use_packsize,
+ unsigned period_frames, unsigned frame_size)
+{
+ int packets, max_packsize;
+ int in_pipe, out_pipe;
+ int read_size = sizeof(struct usb_stream);
+ int write_size;
+ int usb_frames = dev->speed == USB_SPEED_HIGH ? 8000 : 1000;
+ int pg;
+
+ in_pipe = usb_rcvisocpipe(dev, in_endpoint);
+ out_pipe = usb_sndisocpipe(dev, out_endpoint);
+
+ max_packsize = use_packsize ?
+ use_packsize : usb_maxpacket(dev, in_pipe, 0);
+
+ /*
+ t_period = period_frames / sample_rate
+ iso_packs = t_period / t_iso_frame
+ = (period_frames / sample_rate) * (1 / t_iso_frame)
+ */
+
+ packets = period_frames * usb_frames / sample_rate + 1;
+
+ if (dev->speed == USB_SPEED_HIGH)
+ packets = (packets + 7) & ~7;
+
+ read_size += packets * USB_STREAM_URBDEPTH *
+ (max_packsize + sizeof(struct usb_stream_packet));
+
+ max_packsize = usb_maxpacket(dev, out_pipe, 1);
+ write_size = max_packsize * packets * USB_STREAM_URBDEPTH;
+
+ if (read_size >= 256*PAGE_SIZE || write_size >= 256*PAGE_SIZE) {
+ snd_printk(KERN_WARNING "a size exceeds 128*PAGE_SIZE\n");
+ goto out;
+ }
+
+ pg = get_order(read_size);
+ sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+ if (!sk->s) {
+ snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
+ goto out;
+ }
+ sk->s->cfg.version = USB_STREAM_INTERFACE_VERSION;
+
+ sk->s->read_size = read_size;
+
+ sk->s->cfg.sample_rate = sample_rate;
+ sk->s->cfg.frame_size = frame_size;
+ sk->n_o_ps = packets;
+ sk->s->inpackets = packets * USB_STREAM_URBDEPTH;
+ sk->s->cfg.period_frames = period_frames;
+ sk->s->period_size = frame_size * period_frames;
+
+ sk->s->write_size = write_size;
+ pg = get_order(write_size);
+
+ sk->write_page =
+ (void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+ if (!sk->write_page) {
+ snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
+ usb_stream_free(sk);
+ return NULL;
+ }
+
+ /* calculate the frequency in 16.16 format */
+ if (dev->speed == USB_SPEED_FULL)
+ sk->freqn = get_usb_full_speed_rate(sample_rate);
+ else
+ sk->freqn = get_usb_high_speed_rate(sample_rate);
+
+ init_urbs(sk, use_packsize, dev, in_pipe, out_pipe);
+ sk->s->state = usb_stream_stopped;
+out:
+ return sk->s;
+}
+
+
+/* start */
+
+static bool balance_check(struct usb_stream_kernel *sk, struct urb *urb)
+{
+ bool r;
+ if (unlikely(urb->status)) {
+ if (urb->status != -ESHUTDOWN && urb->status != -ENOENT)
+ snd_printk(KERN_WARNING "status=%i\n", urb->status);
+ sk->iso_frame_balance = 0x7FFFFFFF;
+ return false;
+ }
+ r = sk->iso_frame_balance == 0;
+ if (!r)
+ sk->i_urb = urb;
+ return r;
+}
+
+static bool balance_playback(struct usb_stream_kernel *sk, struct urb *urb)
+{
+ sk->iso_frame_balance += urb->number_of_packets;
+ return balance_check(sk, urb);
+}
+
+static bool balance_capture(struct usb_stream_kernel *sk, struct urb *urb)
+{
+ sk->iso_frame_balance -= urb->number_of_packets;
+ return balance_check(sk, urb);
+}
+
+static void subs_set_complete(struct urb **urbs, void (*complete)(struct urb *))
+{
+ int u;
+
+ for (u = 0; u < USB_STREAM_NURBS; u++) {
+ struct urb *urb = urbs[u];
+ urb->complete = complete;
+ }
+}
+
+int usb_stream_prepare_playback(struct usb_stream_kernel *sk, struct urb *inurb)
+{
+ struct usb_stream *s = sk->s;
+ struct urb *io;
+ struct usb_iso_packet_descriptor *id, *od;
+ int p, l = 0;
+
+ io = sk->idle_outurb;
+ od = io->iso_frame_desc;
+ io->transfer_buffer_length = 0;
+
+ for (p = 0; s->sync_packet < 0; ++p, ++s->sync_packet) {
+ struct urb *ii = sk->completed_inurb;
+ id = ii->iso_frame_desc +
+ ii->number_of_packets + s->sync_packet;
+ l = id->actual_length;
+
+ od[p].length = l;
+ od[p].offset = io->transfer_buffer_length;
+ io->transfer_buffer_length += l;
+ }
+
+ for (;
+ s->sync_packet < inurb->number_of_packets && p < sk->n_o_ps;
+ ++p, ++s->sync_packet) {
+ l = inurb->iso_frame_desc[s->sync_packet].actual_length;
+
+ if (s->idle_outsize + io->transfer_buffer_length + l >
+ s->period_size)
+ goto check_ok;
+
+ od[p].length = l;
+ od[p].offset = io->transfer_buffer_length;
+ io->transfer_buffer_length += l;
+ }
+
+check_ok:
+ s->sync_packet -= inurb->number_of_packets;
+ if (s->sync_packet < -2 || s->sync_packet > 0) {
+ snd_printk(KERN_WARNING "invalid sync_packet = %i;"
+ " p=%i nop=%i %i %x %x %x > %x\n",
+ s->sync_packet, p, inurb->number_of_packets,
+ s->idle_outsize + io->transfer_buffer_length + l,
+ s->idle_outsize, io->transfer_buffer_length, l,
+ s->period_size);
+ return -1;
+ }
+ if (io->transfer_buffer_length % s->cfg.frame_size) {
+ snd_printk(KERN_WARNING"invalid outsize = %i\n",
+ io->transfer_buffer_length);
+ return -1;
+ }
+ s->idle_outsize += io->transfer_buffer_length - s->period_size;
+ io->number_of_packets = p;
+ if (s->idle_outsize > 0) {
+ snd_printk(KERN_WARNING "idle=%i\n", s->idle_outsize);
+ return -1;
+ }
+ return 0;
+}
+
+static void prepare_inurb(int number_of_packets, struct urb *iu)
+{
+ struct usb_iso_packet_descriptor *id;
+ int p;
+
+ iu->number_of_packets = number_of_packets;
+ id = iu->iso_frame_desc;
+ id->offset = 0;
+ for (p = 0; p < iu->number_of_packets - 1; ++p)
+ id[p + 1].offset = id[p].offset + id[p].length;
+
+ iu->transfer_buffer_length =
+ id[0].length * iu->number_of_packets;
+}
+
+static int submit_urbs(struct usb_stream_kernel *sk,
+ struct urb *inurb, struct urb *outurb)
+{
+ int err;
+ prepare_inurb(sk->idle_outurb->number_of_packets, sk->idle_inurb);
+ err = usb_submit_urb(sk->idle_inurb, GFP_ATOMIC);
+ if (err < 0) {
+ snd_printk(KERN_ERR "%i\n", err);
+ return err;
+ }
+ sk->idle_inurb = sk->completed_inurb;
+ sk->completed_inurb = inurb;
+ err = usb_submit_urb(sk->idle_outurb, GFP_ATOMIC);
+ if (err < 0) {
+ snd_printk(KERN_ERR "%i\n", err);
+ return err;
+ }
+ sk->idle_outurb = sk->completed_outurb;
+ sk->completed_outurb = outurb;
+ return 0;
+}
+
+#ifdef DEBUG_LOOP_BACK
+/*
+ This loop_back() shows how to read/write the period data.
+ */
+static void loop_back(struct usb_stream *s)
+{
+ char *i, *o;
+ int il, ol, l, p;
+ struct urb *iu;
+ struct usb_iso_packet_descriptor *id;
+
+ o = s->playback1st_to;
+ ol = s->playback1st_size;
+ l = 0;
+
+ if (s->insplit_pack >= 0) {
+ iu = sk->idle_inurb;
+ id = iu->iso_frame_desc;
+ p = s->insplit_pack;
+ } else
+ goto second;
+loop:
+ for (; p < iu->number_of_packets && l < s->period_size; ++p) {
+ i = iu->transfer_buffer + id[p].offset;
+ il = id[p].actual_length;
+ if (l + il > s->period_size)
+ il = s->period_size - l;
+ if (il <= ol) {
+ memcpy(o, i, il);
+ o += il;
+ ol -= il;
+ } else {
+ memcpy(o, i, ol);
+ singen_6pack(o, ol);
+ o = s->playback_to;
+ memcpy(o, i + ol, il - ol);
+ o += il - ol;
+ ol = s->period_size - s->playback1st_size;
+ }
+ l += il;
+ }
+ if (iu == sk->completed_inurb) {
+ if (l != s->period_size)
+ printk(KERN_DEBUG"%s:%i %i\n", __func__, __LINE__,
+ l/(int)s->cfg.frame_size);
+
+ return;
+ }
+second:
+ iu = sk->completed_inurb;
+ id = iu->iso_frame_desc;
+ p = 0;
+ goto loop;
+
+}
+#else
+static void loop_back(struct usb_stream *s)
+{
+}
+#endif
+
+static void stream_idle(struct usb_stream_kernel *sk,
+ struct urb *inurb, struct urb *outurb)
+{
+ struct usb_stream *s = sk->s;
+ int l, p;
+ int insize = s->idle_insize;
+ int urb_size = 0;
+
+ s->inpacket_split = s->next_inpacket_split;
+ s->inpacket_split_at = s->next_inpacket_split_at;
+ s->next_inpacket_split = -1;
+ s->next_inpacket_split_at = 0;
+
+ for (p = 0; p < inurb->number_of_packets; ++p) {
+ struct usb_iso_packet_descriptor *id = inurb->iso_frame_desc;
+ l = id[p].actual_length;
+ if (unlikely(l == 0 || id[p].status)) {
+ snd_printk(KERN_WARNING "underrun, status=%u\n",
+ id[p].status);
+ goto err_out;
+ }
+ s->inpacket_head++;
+ s->inpacket_head %= s->inpackets;
+ if (s->inpacket_split == -1)
+ s->inpacket_split = s->inpacket_head;
+
+ s->inpacket[s->inpacket_head].offset =
+ id[p].offset + (inurb->transfer_buffer - (void *)s);
+ s->inpacket[s->inpacket_head].length = l;
+ if (insize + l > s->period_size &&
+ s->next_inpacket_split == -1) {
+ s->next_inpacket_split = s->inpacket_head;
+ s->next_inpacket_split_at = s->period_size - insize;
+ }
+ insize += l;
+ urb_size += l;
+ }
+ s->idle_insize += urb_size - s->period_size;
+ if (s->idle_insize < 0) {
+ snd_printk(KERN_WARNING "%i\n",
+ (s->idle_insize)/(int)s->cfg.frame_size);
+ goto err_out;
+ }
+ s->insize_done += urb_size;
+
+ l = s->idle_outsize;
+ s->outpacket[0].offset = (sk->idle_outurb->transfer_buffer -
+ sk->write_page) - l;
+
+ if (usb_stream_prepare_playback(sk, inurb) < 0)
+ goto err_out;
+
+ s->outpacket[0].length = sk->idle_outurb->transfer_buffer_length + l;
+ s->outpacket[1].offset = sk->completed_outurb->transfer_buffer -
+ sk->write_page;
+
+ if (submit_urbs(sk, inurb, outurb) < 0)
+ goto err_out;
+
+ loop_back(s);
+ s->periods_done++;
+ wake_up_all(&sk->sleep);
+ return;
+err_out:
+ s->state = usb_stream_xrun;
+ wake_up_all(&sk->sleep);
+}
+
+static void i_capture_idle(struct urb *urb)
+{
+ struct usb_stream_kernel *sk = urb->context;
+ if (balance_capture(sk, urb))
+ stream_idle(sk, urb, sk->i_urb);
+}
+
+static void i_playback_idle(struct urb *urb)
+{
+ struct usb_stream_kernel *sk = urb->context;
+ if (balance_playback(sk, urb))
+ stream_idle(sk, sk->i_urb, urb);
+}
+
+static void stream_start(struct usb_stream_kernel *sk,
+ struct urb *inurb, struct urb *outurb)
+{
+ struct usb_stream *s = sk->s;
+ if (s->state >= usb_stream_sync1) {
+ int l, p, max_diff, max_diff_0;
+ int urb_size = 0;
+ unsigned frames_per_packet, min_frames = 0;
+ frames_per_packet = (s->period_size - s->idle_insize);
+ frames_per_packet <<= 8;
+ frames_per_packet /=
+ s->cfg.frame_size * inurb->number_of_packets;
+ frames_per_packet++;
+
+ max_diff_0 = s->cfg.frame_size;
+ if (s->cfg.period_frames >= 256)
+ max_diff_0 <<= 1;
+ if (s->cfg.period_frames >= 1024)
+ max_diff_0 <<= 1;
+ max_diff = max_diff_0;
+ for (p = 0; p < inurb->number_of_packets; ++p) {
+ int diff;
+ l = inurb->iso_frame_desc[p].actual_length;
+ urb_size += l;
+
+ min_frames += frames_per_packet;
+ diff = urb_size -
+ (min_frames >> 8) * s->cfg.frame_size;
+ if (diff < max_diff) {
+ snd_printdd(KERN_DEBUG "%i %i %i %i\n",
+ s->insize_done,
+ urb_size / (int)s->cfg.frame_size,
+ inurb->number_of_packets, diff);
+ max_diff = diff;
+ }
+ }
+ s->idle_insize -= max_diff - max_diff_0;
+ s->idle_insize += urb_size - s->period_size;
+ if (s->idle_insize < 0) {
+ snd_printk("%i %i %i\n",
+ s->idle_insize, urb_size, s->period_size);
+ return;
+ } else if (s->idle_insize == 0) {
+ s->next_inpacket_split =
+ (s->inpacket_head + 1) % s->inpackets;
+ s->next_inpacket_split_at = 0;
+ } else {
+ unsigned split = s->inpacket_head;
+ l = s->idle_insize;
+ while (l > s->inpacket[split].length) {
+ l -= s->inpacket[split].length;
+ if (split == 0)
+ split = s->inpackets - 1;
+ else
+ split--;
+ }
+ s->next_inpacket_split = split;
+ s->next_inpacket_split_at =
+ s->inpacket[split].length - l;
+ }
+
+ s->insize_done += urb_size;
+
+ if (usb_stream_prepare_playback(sk, inurb) < 0)
+ return;
+
+ } else
+ playback_prep_freqn(sk, sk->idle_outurb);
+
+ if (submit_urbs(sk, inurb, outurb) < 0)
+ return;
+
+ if (s->state == usb_stream_sync1 && s->insize_done > 360000) {
+ /* just guesswork ^^^^^^ */
+ s->state = usb_stream_ready;
+ subs_set_complete(sk->inurb, i_capture_idle);
+ subs_set_complete(sk->outurb, i_playback_idle);
+ }
+}
+
+static void i_capture_start(struct urb *urb)
+{
+ struct usb_iso_packet_descriptor *id = urb->iso_frame_desc;
+ struct usb_stream_kernel *sk = urb->context;
+ struct usb_stream *s = sk->s;
+ int p;
+ int empty = 0;
+
+ if (urb->status) {
+ snd_printk(KERN_WARNING "status=%i\n", urb->status);
+ return;
+ }
+
+ for (p = 0; p < urb->number_of_packets; ++p) {
+ int l = id[p].actual_length;
+ if (l < s->cfg.frame_size) {
+ ++empty;
+ if (s->state >= usb_stream_sync0) {
+ snd_printk(KERN_WARNING "%i\n", l);
+ return;
+ }
+ }
+ s->inpacket_head++;
+ s->inpacket_head %= s->inpackets;
+ s->inpacket[s->inpacket_head].offset =
+ id[p].offset + (urb->transfer_buffer - (void *)s);
+ s->inpacket[s->inpacket_head].length = l;
+ }
+#ifdef SHOW_EMPTY
+ if (empty) {
+ printk(KERN_DEBUG"%s:%i: %i", __func__, __LINE__,
+ urb->iso_frame_desc[0].actual_length);
+ for (pack = 1; pack < urb->number_of_packets; ++pack) {
+ int l = urb->iso_frame_desc[pack].actual_length;
+ printk(" %i", l);
+ }
+ printk("\n");
+ }
+#endif
+ if (!empty && s->state < usb_stream_sync1)
+ ++s->state;
+
+ if (balance_capture(sk, urb))
+ stream_start(sk, urb, sk->i_urb);
+}
+
+static void i_playback_start(struct urb *urb)
+{
+ struct usb_stream_kernel *sk = urb->context;
+ if (balance_playback(sk, urb))
+ stream_start(sk, sk->i_urb, urb);
+}
+
+int usb_stream_start(struct usb_stream_kernel *sk)
+{
+ struct usb_stream *s = sk->s;
+ int frame = 0, iters = 0;
+ int u, err;
+ int try = 0;
+
+ if (s->state != usb_stream_stopped)
+ return -EAGAIN;
+
+ subs_set_complete(sk->inurb, i_capture_start);
+ subs_set_complete(sk->outurb, i_playback_start);
+ memset(sk->write_page, 0, s->write_size);
+dotry:
+ s->insize_done = 0;
+ s->idle_insize = 0;
+ s->idle_outsize = 0;
+ s->sync_packet = -1;
+ s->inpacket_head = -1;
+ sk->iso_frame_balance = 0;
+ ++try;
+ for (u = 0; u < 2; u++) {
+ struct urb *inurb = sk->inurb[u];
+ struct urb *outurb = sk->outurb[u];
+ playback_prep_freqn(sk, outurb);
+ inurb->number_of_packets = outurb->number_of_packets;
+ inurb->transfer_buffer_length =
+ inurb->number_of_packets *
+ inurb->iso_frame_desc[0].length;
+ preempt_disable();
+ if (u == 0) {
+ int now;
+ struct usb_device *dev = inurb->dev;
+ frame = usb_get_current_frame_number(dev);
+ do {
+ now = usb_get_current_frame_number(dev);
+ ++iters;
+ } while (now > -1 && now == frame);
+ }
+ err = usb_submit_urb(inurb, GFP_ATOMIC);
+ if (err < 0) {
+ preempt_enable();
+ snd_printk(KERN_ERR"usb_submit_urb(sk->inurb[%i])"
+ " returned %i\n", u, err);
+ return err;
+ }
+ err = usb_submit_urb(outurb, GFP_ATOMIC);
+ if (err < 0) {
+ preempt_enable();
+ snd_printk(KERN_ERR"usb_submit_urb(sk->outurb[%i])"
+ " returned %i\n", u, err);
+ return err;
+ }
+ preempt_enable();
+ if (inurb->start_frame != outurb->start_frame) {
+ snd_printd(KERN_DEBUG
+ "u[%i] start_frames differ in:%u out:%u\n",
+ u, inurb->start_frame, outurb->start_frame);
+ goto check_retry;
+ }
+ }
+ snd_printdd(KERN_DEBUG "%i %i\n", frame, iters);
+ try = 0;
+check_retry:
+ if (try) {
+ usb_stream_stop(sk);
+ if (try < 5) {
+ msleep(1500);
+ snd_printd(KERN_DEBUG "goto dotry;\n");
+ goto dotry;
+ }
+ snd_printk(KERN_WARNING"couldn't start"
+ " all urbs on the same start_frame.\n");
+ return -EFAULT;
+ }
+
+ sk->idle_inurb = sk->inurb[USB_STREAM_NURBS - 2];
+ sk->idle_outurb = sk->outurb[USB_STREAM_NURBS - 2];
+ sk->completed_inurb = sk->inurb[USB_STREAM_NURBS - 1];
+ sk->completed_outurb = sk->outurb[USB_STREAM_NURBS - 1];
+
+/* wait, check */
+ {
+ int wait_ms = 3000;
+ while (s->state != usb_stream_ready && wait_ms > 0) {
+ snd_printdd(KERN_DEBUG "%i\n", s->state);
+ msleep(200);
+ wait_ms -= 200;
+ }
+ }
+
+ return s->state == usb_stream_ready ? 0 : -EFAULT;
+}
+
+
+/* stop */
+
+void usb_stream_stop(struct usb_stream_kernel *sk)
+{
+ int u;
+ if (!sk->s)
+ return;
+ for (u = 0; u < USB_STREAM_NURBS; ++u) {
+ usb_kill_urb(sk->inurb[u]);
+ usb_kill_urb(sk->outurb[u]);
+ }
+ sk->s->state = usb_stream_stopped;
+ msleep(400);
+}
diff --git a/sound/usb/usx2y/usb_stream.h b/sound/usb/usx2y/usb_stream.h
new file mode 100644
index 00000000000..4dd74ab1e9c
--- /dev/null
+++ b/sound/usb/usx2y/usb_stream.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define USB_STREAM_INTERFACE_VERSION 2
+
+#define SNDRV_USB_STREAM_IOCTL_SET_PARAMS \
+ _IOW('H', 0x90, struct usb_stream_config)
+
+struct usb_stream_packet {
+ unsigned offset;
+ unsigned length;
+};
+
+
+struct usb_stream_config {
+ unsigned version;
+ unsigned sample_rate;
+ unsigned period_frames;
+ unsigned frame_size;
+};
+
+struct usb_stream {
+ struct usb_stream_config cfg;
+ unsigned read_size;
+ unsigned write_size;
+
+ int period_size;
+
+ unsigned state;
+
+ int idle_insize;
+ int idle_outsize;
+ int sync_packet;
+ unsigned insize_done;
+ unsigned periods_done;
+ unsigned periods_polled;
+
+ struct usb_stream_packet outpacket[2];
+ unsigned inpackets;
+ unsigned inpacket_head;
+ unsigned inpacket_split;
+ unsigned inpacket_split_at;
+ unsigned next_inpacket_split;
+ unsigned next_inpacket_split_at;
+ struct usb_stream_packet inpacket[0];
+};
+
+enum usb_stream_state {
+ usb_stream_invalid,
+ usb_stream_stopped,
+ usb_stream_sync0,
+ usb_stream_sync1,
+ usb_stream_ready,
+ usb_stream_running,
+ usb_stream_xrun,
+};
+
+#if __KERNEL__
+
+#define USB_STREAM_NURBS 4
+#define USB_STREAM_URBDEPTH 4
+
+struct usb_stream_kernel {
+ struct usb_stream *s;
+
+ void *write_page;
+
+ unsigned n_o_ps;
+
+ struct urb *inurb[USB_STREAM_NURBS];
+ struct urb *idle_inurb;
+ struct urb *completed_inurb;
+ struct urb *outurb[USB_STREAM_NURBS];
+ struct urb *idle_outurb;
+ struct urb *completed_outurb;
+ struct urb *i_urb;
+
+ int iso_frame_balance;
+
+ wait_queue_head_t sleep;
+
+ unsigned out_phase;
+ unsigned out_phase_peeked;
+ unsigned freqn;
+};
+
+struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
+ struct usb_device *dev,
+ unsigned in_endpoint, unsigned out_endpoint,
+ unsigned sample_rate, unsigned use_packsize,
+ unsigned period_frames, unsigned frame_size);
+void usb_stream_free(struct usb_stream_kernel *);
+int usb_stream_start(struct usb_stream_kernel *);
+void usb_stream_stop(struct usb_stream_kernel *);
+
+
+#endif